Skip to content

Commit

Permalink
Merge pull request #78 from AmiyaBot/feature
Browse files Browse the repository at this point in the history
Feature
  • Loading branch information
vivien8261 authored Nov 14, 2023
2 parents 6e7b3c3 + 504c47d commit 425b7ec
Show file tree
Hide file tree
Showing 33 changed files with 443 additions and 356 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/dist/
/venv/
/log/
/testTemp/
/*.egg-info/

.DS_Store
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ asyncio.run(bots.start())
import asyncio

from amiyabot import AmiyaBot, Message, Chain
from amiyabot.adapters.onebot11 import onebot11
from amiyabot.adapters.onebot.v11 import onebot11

bot = AmiyaBot(
appid='******',
Expand Down
14 changes: 7 additions & 7 deletions amiyabot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
from amiyabot.adapters.kook import KOOKBotInstance
from amiyabot.adapters.mirai import MiraiBotInstance
from amiyabot.adapters.cqhttp import CQHttpBotInstance
from amiyabot.adapters.tencent import TencentBotInstance
from amiyabot.adapters.onebot.v11 import OneBot11Instance
from amiyabot.adapters.onebot.v12 import OneBot12Instance
from amiyabot.adapters.tencent.qqGuild import QQGuildBotInstance, QQGuildSandboxBotInstance
from amiyabot.adapters.comwechat import ComWeChatBotInstance
from amiyabot.adapters.common import CQCode

# network
Expand All @@ -27,12 +30,10 @@
# lib
from amiyabot.builtin.lib.eventBus import event_bus
from amiyabot.builtin.lib.timedTask import TasksControl
from amiyabot.builtin.lib.browserService import (
BrowserLaunchConfig,
basic_browser_service,
)
from amiyabot.builtin.lib.browserService import BrowserLaunchConfig, basic_browser_service

# message
from amiyabot.builtin.messageChain import Chain, ChainBuilder
from amiyabot.builtin.message import (
Event,
EventList,
Expand All @@ -42,7 +43,6 @@
WaitEventOutOfFocus,
Equal,
)
from amiyabot.builtin.messageChain import Chain, ChainBuilder


class AmiyaBot(BotInstance):
Expand All @@ -51,7 +51,7 @@ def __init__(
appid: Optional[str] = None,
token: Optional[str] = None,
private: bool = False,
adapter: typing.Type[BotAdapterProtocol] = TencentBotInstance,
adapter: typing.Type[BotAdapterProtocol] = QQGuildBotInstance,
):
if not appid:
appid = random_code(10)
Expand Down
2 changes: 1 addition & 1 deletion amiyabot/adapters/comwechat/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio

from amiyabot.adapters import HANDLER_TYPE
from amiyabot.adapters.onebot12 import OneBot12Instance
from amiyabot.adapters.onebot.v12 import OneBot12Instance
from amiyabot.builtin.messageChain import Chain

from .package import package_com_wechat_message
Expand Down
2 changes: 1 addition & 1 deletion amiyabot/adapters/comwechat/builder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from amiyabot.adapters import MessageCallback
from amiyabot.adapters.apiProtocol import BotInstanceAPIProtocol
from amiyabot.adapters.onebot12.builder import build_message_send as build_ob12
from amiyabot.adapters.onebot.v12 import build_message_send as build_ob12
from amiyabot.builtin.message import Message
from amiyabot.builtin.messageChain import Chain
from amiyabot.builtin.messageChain.element import *
Expand Down
2 changes: 1 addition & 1 deletion amiyabot/adapters/comwechat/package.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from amiyabot.adapters import BotAdapterProtocol
from amiyabot.adapters.onebot12.package import package_onebot12_message
from amiyabot.adapters.onebot.v12 import package_onebot12_message


async def package_com_wechat_message(instance: BotAdapterProtocol, data: dict):
Expand Down
2 changes: 1 addition & 1 deletion amiyabot/adapters/cqhttp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from amiyabot.adapters.onebot11 import OneBot11Instance
from amiyabot.adapters.onebot.v11 import OneBot11Instance

from .api import CQHttpAPI
from .forwardMessage import CQHTTPForwardMessage
Expand Down
2 changes: 1 addition & 1 deletion amiyabot/adapters/cqhttp/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Optional

from amiyabot.adapters.onebot11.api import OneBot11API
from amiyabot.adapters.onebot.v11.api import OneBot11API
from amiyabot.adapters.mirai.api import MiraiAPI


Expand Down
2 changes: 1 addition & 1 deletion amiyabot/adapters/cqhttp/forwardMessage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Union, Optional
from amiyabot.builtin.message import Message
from amiyabot.builtin.messageChain import Chain
from amiyabot.adapters.onebot11.builder import build_message_send, OneBot11MessageCallback
from amiyabot.adapters.onebot.v11.builder import build_message_send, OneBot11MessageCallback

from .api import CQHttpAPI

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from amiyabot.builtin.message import Event, EventList, Message
from amiyabot.adapters import BotAdapterProtocol

from ..common import text_convert
from amiyabot.adapters.common import text_convert


async def package_onebot11_message(instance: BotAdapterProtocol, account: str, data: dict):
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from amiyabot.builtin.message import Event, EventList, Message
from amiyabot.adapters import BotAdapterProtocol

from ..common import text_convert
from amiyabot.adapters.common import text_convert
from .api import OneBot12API


Expand Down
242 changes: 13 additions & 229 deletions amiyabot/adapters/tencent/__init__.py
Original file line number Diff line number Diff line change
@@ -1,235 +1,19 @@
import sys
import json
import asyncio
from .qqGuild import QQGuildBotInstance, QQGuildSandboxBotInstance

from websockets.legacy.client import WebSocketClientProtocol
from typing import Dict, Optional
from amiyabot.log import LoggerManager
from amiyabot.util import random_code
from amiyabot.builtin.message import Message
from amiyabot.builtin.messageChain import Chain
from amiyabot.adapters import BotAdapterProtocol, HANDLER_TYPE

from .api import TencentAPI
from .model import GateWay, Payload, ShardsRecord, ConnectionHandler
from .intents import Intents
from .package import package_tencent_message
from .builder import build_message_send, TencentMessageCallback
class TencentBotInstance(QQGuildBotInstance):
...

log = LoggerManager('Tencent')

class TencentSandboxBotInstance(QQGuildSandboxBotInstance):
...

class TencentBotInstance(BotAdapterProtocol):
def __init__(self, appid: str, token: str):
super().__init__(appid, token)

self.appid = appid
self.token = token

self.shards_record: Dict[int, ShardsRecord] = {}

def __str__(self):
return 'Tencent'

@property
def api(self):
return TencentAPI(self.appid, self.token)

def __create_heartbeat(self, websocket, interval: int, record: ShardsRecord):
heartbeat_key = random_code(10)
record.heartbeat_key = heartbeat_key
asyncio.create_task(self.heartbeat_interval(websocket, interval, record.shards_index, heartbeat_key))

async def close(self):
log.info(f'closing {self}(appid {self.appid})...')
self.keep_run = False

for _, item in self.shards_record.items():
if item.connection:
await item.connection.close()

async def start(self, private: bool, handler: HANDLER_TYPE):
log.info(f'requesting appid {self.appid} gateway')

resp = await self.api.gateway_bot()

if not resp:
if self.keep_run:
await asyncio.sleep(10)
asyncio.create_task(self.start(private, handler))
return False

gateway = GateWay(**resp.json)

log.info(
f'appid {self.appid} gateway resp: shards {gateway.shards}, remaining %d/%d'
% (
gateway.session_start_limit['remaining'],
gateway.session_start_limit['total'],
)
)

await self.create_connection(ConnectionHandler(private=private, gateway=gateway, message_handler=handler))

async def create_connection(self, handler: ConnectionHandler, shards_index: int = 0):
gateway = handler.gateway
sign = f'{self.appid} {shards_index + 1}/{gateway.shards}'

async with self.get_websocket_connection(sign, gateway.url) as websocket:
if websocket:
self.shards_record[shards_index] = ShardsRecord(shards_index, connection=websocket)

while self.keep_run:
await asyncio.sleep(0)

recv = await websocket.recv()
payload = Payload(**json.loads(recv))

if payload.op == 0:
if payload.t == 'READY':
log.info(
f'connected({sign}): %s(%s)'
% (
payload.d['user']['username'],
'private' if handler.private else 'public',
)
)
self.shards_record[shards_index].session_id = payload.d['session_id']

if shards_index == 0 and gateway.shards > 1:
for n in range(gateway.shards - 1):
asyncio.create_task(self.create_connection(handler, n + 1))
else:
await self.create_package_task(handler, payload)

if payload.op == 10:
create_token = {
'token': f'Bot {self.appid}.{self.token}',
'intents': Intents(handler.private).intents.get_all_intents(),
'shard': [shards_index, gateway.shards],
'properties': {
'$os': sys.platform,
'$browser': '',
'$device': '',
},
}
await websocket.send(Payload(op=2, d=create_token).to_json())

self.__create_heartbeat(
websocket,
payload.d['heartbeat_interval'],
self.shards_record[shards_index],
)

if payload.s:
self.shards_record[shards_index].last_s = payload.s

while self.keep_run and self.shards_record[shards_index].reconnect_limit > 0:
await self.reconnect(handler, self.shards_record[shards_index], sign)
await asyncio.sleep(1)

async def reconnect(self, handler: ConnectionHandler, record: ShardsRecord, sign: str):
log.info(f'reconnecting({sign})...')

async with self.get_websocket_connection(sign, handler.gateway.url) as websocket:
if websocket:
record.connection = websocket

while self.keep_run:
await asyncio.sleep(0)

recv = await websocket.recv()
payload = Payload(**json.loads(recv))

if payload.op == 0:
if payload.t == 'RESUMED':
log.info(f'Bot reconnected({sign}).')
else:
await self.create_package_task(handler, payload)

if payload.op == 10:
reconnect_token = {
'token': f'Bot {self.appid}.{self.token}',
'session_id': record.session_id,
'seq': record.last_s,
}
await websocket.send(Payload(op=6, d=reconnect_token).to_json())

self.__create_heartbeat(websocket, payload.d['heartbeat_interval'], record)

record.reconnect_limit = 3

if payload.s:
record.last_s = payload.s

record.reconnect_limit -= 1

async def heartbeat_interval(
self,
websocket: WebSocketClientProtocol,
interval: int,
shards_index: int,
heartbeat_key: str,
):
sec = 0
while self.keep_run and self.shards_record[shards_index].heartbeat_key == heartbeat_key:
await asyncio.sleep(1)
sec += 1
if sec >= interval / 1000:
sec = 0
await websocket.send(Payload(op=1, d=self.shards_record[shards_index].last_s).to_json())

async def create_package_task(self, handler: ConnectionHandler, payload: Payload):
asyncio.create_task(
handler.message_handler(
await package_tencent_message(self, payload.t, payload.d),
),
)

async def send_chain_message(self, chain: Chain, is_sync: bool = False):
reqs = await build_message_send(chain)
res = []

for req in reqs.req_list:
async with log.catch('post error:', ignore=[asyncio.TimeoutError]):
res.append(
await self.api.post_message(
chain.data.guild_id,
chain.data.src_guild_id,
chain.data.channel_id,
req,
)
)

return [TencentMessageCallback(chain.data, self, item) for item in res]

async def build_active_message_chain(self, chain: Chain, user_id: str, channel_id: str, direct_src_guild_id: str):
data = Message(self)

data.user_id = user_id
data.channel_id = channel_id

if not channel_id and not direct_src_guild_id:
raise TypeError('send_message() missing argument: "channel_id" or "direct_src_guild_id"')

if direct_src_guild_id:
if not user_id:
raise TypeError('send_message(direct_src_guild_id=...) missing argument: "user_id"')

data.is_direct = True
data.src_guild_id = direct_src_guild_id

message = Chain(data)
message.chain = chain.chain
message.builder = chain.builder

return message

async def recall_message(self, message_id: str, data: Optional[Message] = None):
await self.api.delete_message(message_id, data.guild_id if data.is_direct else data.channel_id, data.is_direct)


class TencentSandboxBotInstance(TencentBotInstance):
@property
def api(self):
return TencentAPI(self.appid, self.token, True)
notice = (
'"{name}" is deprecated and will be removed in future versions. '
'Please using "from amiyabot.adapters.tencent.qqGuild import {new_name}"'
)
print('\n==== Warning ===============================================')
print(notice.format(name=TencentBotInstance.__name__, new_name=QQGuildBotInstance.__name__))
print(notice.format(name=TencentSandboxBotInstance.__name__, new_name=QQGuildSandboxBotInstance.__name__))
print('============================================================\n')
Loading

0 comments on commit 425b7ec

Please sign in to comment.