From 75ec2af02708a9f2de122d7b9fef25d55b6dd464 Mon Sep 17 00:00:00 2001 From: Celely Date: Fri, 28 Apr 2023 20:40:04 +0900 Subject: [PATCH 1/5] =?UTF-8?q?SDK=E3=81=AE=E5=A4=A7=E8=A6=8F=E6=A8=A1?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sdk/LICENSE | 21 ++++++ sdk/__init__.py | 137 +------------------------------------ sdk/gateway.py | 176 ++++++++++++++++++++++++++++++++++++++++++++---- sdk/items.py | 21 +++++- 4 files changed, 202 insertions(+), 153 deletions(-) create mode 100644 sdk/LICENSE diff --git a/sdk/LICENSE b/sdk/LICENSE new file mode 100644 index 0000000..4f765fe --- /dev/null +++ b/sdk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Union Global Chat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/__init__.py b/sdk/__init__.py index 5da5fb6..1af9130 100644 --- a/sdk/__init__.py +++ b/sdk/__init__.py @@ -1,137 +1,2 @@ -from websockets import connect, exceptions -from time import time from .items import * -import discord -import asyncio -import zlib -import httpx -import orjson - - -class Error(Exception): - pass - - -class Client: - def __init__(self, token: str): - self.token = token - self.on_event = {} - self.client = httpx.AsyncClient(base_url="https://ugc.renorari.net/api/v2") - - async def close(self): - self.ws = None - await self.client.aclose() - await self.ws.close() - - async def connect(self): - self.ws = await connect("wss://ugc.renorari.net/api/v2/gateway") - while self.open: - await self.recv() - - async def request(self, method: str, path: str, *args, **kwargs): - kwargs["headers"] = { - "Authorization": "Bearer {}".format(self.token) - } - r = await self.client.request(method, path, *args, **kwargs) - if r.status_code == 404: - raise Error("404エラー") - elif r.status_code == 200: - return r.json() - elif r.status_code == 401: - raise Error("認証エラー") - elif r.status_code == 400: - raise Error(r.json()["message"]) - - @property - def open(self) -> True: - return self.ws.open - - @property - def latency(self): - return self._heartbeat - - async def on_close(self): - self.dispatch("close") - await asyncio.sleep(5) - self.ws = None - await self.connect() - - async def recv(self): - try: - data = orjson.loads(zlib.decompress(await self.ws.recv())) - except exceptions.ConnectionClosed: - await self.on_close() - else: - if data["type"] == "hello": - await self.identify() - elif data["type"] == "identify": - if data["success"]: - self.dispatch("ready") - elif data["type"] == "message": - self.dispatch("message", Message( - data["data"]["data"], data["data"]["source"])) - elif data["type"] == "heartbeat": - self._heartbeat = time() - data["data"]["unix_time"] - - async def identify(self): - await self.ws_send("identify", {"token": self.token}) - - def on(self, name: str): - def deco(coro): - if name in self.on_event: - self.on_event[name].append(coro) - else: - self.on_event[name] = [coro] - return coro - return deco - - def dispatch(self, name: str, *args): - if name in self.on_event: - for coro in self.on_event[name]: - asyncio.create_task(coro(*args)) - - async def ws_send(self, type: str, data: dict): - payload = {"type": type, "data": data} - await self.ws.send(zlib.compress(orjson.dumps(payload))) - - async def send(self, message: discord.Message): - payload = { - "channel": { - "name": message.channel.name, - "id": str(message.channel.id) - }, - "author": { - "username": message.author.name, - "discriminator": message.author.discriminator, - "id": str(message.author.id), - "avatarURL": getattr(message.author.avatar, "url", None), - "bot": message.author.bot - }, - "guild": { - "name": message.guild.name, - "id": str(message.guild.id), - "iconURL": getattr(message.guild.icon, "url", None) - }, - "message": { - "content": message.content, - "id": str(message.id), - "cleanContent": message.clean_content, - "embeds": [], - "attachments": [ - { - "url": attachment.url, - "name": attachment.filename, - "width": str(attachment.width), - "height": str(attachment.height), - "content_type": attachment.content_type - } - for attachment in message.attachments - ], - "reference": { - "channel_id": None, - "guild_id": None, - "message_id": None - } - } - } - await self.request("POST", "/messages", json=payload) +from .gateway import Client \ No newline at end of file diff --git a/sdk/gateway.py b/sdk/gateway.py index a4c5cd7..ce7066a 100644 --- a/sdk/gateway.py +++ b/sdk/gateway.py @@ -1,6 +1,12 @@ -from websockets import connect, WebSocketClientProtocol +import time +import asyncio +import discord +from websockets import client +from websockets.exceptions import ConnectionClosed +from .items import Message -from typing import Optional + +import aiohttp try: import orjson as json @@ -9,20 +15,162 @@ import zlib -class UgcGateway: - - GATEWAY_URL: str = "wss://ugc.renorari.net/api/v2/gateway" - def __init__(self): - self.ws: Optional[WebSocketClientProtocol] = None +gateway_url = "wss://ugc.renorari.net/api/v2/gateway" +message_url = "https://ugc.renorari.net/api/v2/messages" + + + +class Error(Exception): + pass + + +class Client: + def __init__(self, token: str): + self.token = token + self.on_event = {} + + async def close(self): + self.ws = None + await self.client.aclose() + await self.ws.close() async def connect(self): - self.ws = await connect(self.GATEWAY_URL) + async with client.connect(gateway_url) as ws: + self.ws = ws + + while self.open: + await self.recv() + + + async def request(self, json_data): + json_data["headers"] = {"Authorization": "Bearer {}".format(self.token), + "Content-Type": "application/json"} + + async with aiohttp.ClientSession() as session: + async with session.post(message_url, data = json.dumps(json_data)) as r: + print(r.status) + if r.status == 404: + raise Error("404エラー") + elif r.status == 200: + return r.json() + elif r.status == 401: + raise Error("認証エラー") + elif r.status == 400: + raise Error(r.json()["message"]) + + + async def send_message(self, message : discord.Message): + message = json.dumps(self.discord_message_to_ugc_message(message)) + header = {"Authorization": "Bearer {}".format(self.token), + "Content-Type": "application/json"} + + async with aiohttp.ClientSession() as session: + async with session.post(message_url, data = message, headers = header) as r: + if r.status == 200: + return await r.json() + else: + print(r.status) + + + @property + def open(self) -> bool: + return self.ws.open + + @property + def latency(self): + return self._heartbeat + + async def on_close(self): + self.dispatch("close") + await asyncio.sleep(5) + self.ws = None + await self.connect() + + async def recv(self): + try: + data = json.loads(zlib.decompress(await self.ws.recv())) + + except ConnectionClosed: + await self.on_close() + + else: + if data["type"] == "hello": + await self.identify() + elif data["type"] == "identify": + if data["success"]: + self.dispatch("ready") + elif data["type"] == "message": + self.dispatch("message", Message( + data["data"]["data"], data["data"]["source"])) + elif data["type"] == "heartbeat": + self._heartbeat = time.time() - data["data"]["unix_time"] + + async def identify(self): + await self.ws_send("identify", {"token": self.token}) + + + def on(self, name: str): + def deco(coro): + if name in self.on_event: + self.on_event[name].append(coro) + else: + self.on_event[name] = [coro] + return coro + return deco + + + def dispatch(self, name: str, *args): + if name in self.on_event: + for coro in self.on_event[name]: + asyncio.create_task(coro(*args)) + + + async def ws_send(self, type: str, data: dict): + payload = {"type": type, "data": data} + await self.ws.send(zlib.compress(json.dumps(payload))) + - async def recieve_message(self): - payload = json.loads(zlib.decompress(await self.ws.recv())) + def discord_message_to_ugc_message(self, message : discord.Message): + attachments = [] + for attachment in message.attachments: - if payload["type"] == "identify": - self.heartbeat = HeartBeat(ws=self.ws) + attachments_dict = { + "url" : attachment.url, + "name" : attachment.filename, + "width": str(attachment.width), + "height": str(attachment.height), + "content_type": attachment.content_type + } + attachments.append(attachments_dict) - def close(self): - return self.ws.close() + message_dict = { + "channel": { + "name": message.channel.name, + "id": str(message.channel.id) + }, + "author": { + "username": message.author.name, + "discriminator": message.author.discriminator, + "id": str(message.author.id), + "avatarURL": message.author.display_avatar.url, + "bot": message.author.bot + }, + "guild": { + "name": message.guild.name, + "id": str(message.guild.id), + "iconURL": getattr(message.guild.icon, "url", None) + }, + "message": { + "content": message.content, + "id": str(message.id), + "cleanContent": message.clean_content, + "embeds": [], + "attachments": attachments, + "reference": { + "channel_id": str(message.channel.id), + "guild_id": str(message.guild.id), + "message_id": str(message.id) + } + } + } + return message_dict \ No newline at end of file diff --git a/sdk/items.py b/sdk/items.py index 8dbd0ac..e517fd4 100644 --- a/sdk/items.py +++ b/sdk/items.py @@ -1,12 +1,27 @@ class Message: def __init__(self, data: dict, from_: str): - print(data) self.data = data + self.content = data["message"]["content"] self.source = from_ self.channel = Channel(data["channel"]) self.author = User(data["author"]) self.guild = Guild(data["guild"]) - self.content = data["message"]["content"] + + self.attachments : list[Attachments] = [] + for a in data["message"]["attachments"]: + self.attachments.append( + Attachments(a) + ) + + + +class Attachments: + def __init__(self, data: dict): + self.url = data["url"] + self.name = data["name"] + self.width = data["width"] + self.height = data["height"] + self.content_type = data["content_type"] class Channel: @@ -28,4 +43,4 @@ class Guild: def __init__(self, data: dict): self.id = data["id"] self.name = data["name"] - self.iconURL = data["iconURL"] + self.icon_url = data["iconURL"] From 16ea7948e451340b15c8c546f9954439138b7b6b Mon Sep 17 00:00:00 2001 From: Celely Date: Fri, 28 Apr 2023 20:54:50 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 251 +++++++++++++++++++++++++++++++++++----------------- sdk/LICENSE | 21 ----- 2 files changed, 168 insertions(+), 104 deletions(-) delete mode 100644 sdk/LICENSE diff --git a/main.py b/main.py index af82645..6539047 100644 --- a/main.py +++ b/main.py @@ -1,108 +1,193 @@ import discord -from discord import app_commands -from sdk import Client as SdkClient -from base64 import b64encode, b64decode -from json import load, dumps, loads +from discord.ext import commands +from discord import Webhook, app_commands +from sdk import Client, Message import asyncio -try: - import uvloop -except ImportError: - uvloop.install() +import json +import re +import aiohttp -class MyClient(discord.Client): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - with open("config.json", "r") as f: - self.config = load(f) - self.sdk = SdkClient(self.config["ugc"]) +bot = commands.Bot(intents=discord.Intents.all()) - def on(self, name): - return self.sdk.on(name) - async def close(self): - await self.sdk.close() - await super().close() +sdk = Client("token here") -client = MyClient(intents=discord.Intents.all()) -tree = app_commands.CommandTree(client) +@bot.tree.command(description="Pong") +async def ping(interaction): + await interaction.response.send_message("{}ms".format(round(sdk.latency * 1000))) -@client.event -async def on_ready(): - print("ready") - await tree.sync() - await client.sdk.connect() - await client.sdk.reconnect() +invite_pattern = re.compile("(https?://)?((ptb|canary)\.)?(discord\.(gg|io)|discord(app)?.com/invite)/[0-9a-zA-Z]+", re.IGNORECASE) +token_pattern = re.compile("[A-Za-z0-9\-_]{23,30}\.[A-Za-z0-9\-_]{6,7}\.[A-Za-z0-9\-_]{27,40}", re.IGNORECASE) - -@tree.command(description="Pong") -async def ping(interaction): - await interaction.response.send_message("{}ms".format(round(client.sdk.latency * 1000))) +async def message_check(message) -> bool: + dis_tok = token_pattern.search(message.content) + invite_link = invite_pattern.search(message.content) + if dis_tok: + embed = discord.Embed(description = "Discord認証トークンをグローバルチャットに送信することはできません。", colour = discord.Colour.red()) -@client.on("ready") -async def ready_for_ugc(): - print("Ready for ugc") + await message.channel.send(embed=embed, reference=message) + await message.add_reaction('❌') + return True + + elif invite_link: + embed = discord.Embed(description = "Discordの招待リンクをグローバルチャットに送信することはできません。", colour = discord.Colour.red()) + + await message.channel.send(embed = embed, reference=message) + await message.add_reaction('❌') + return True + + else: + return False + + +def json_load(path) -> dict: + with open(path, "r", encoding="utf-8") as f: + load = json.load(f) + return load + + +@bot.tree.command(name="global", + description="グローバルチャットを作成します。すでに作成されている場合はできません。") +@app_commands.checks.has_permissions(manage_channels=True) +async def gc_join(ctx): + load = json_load("./data/json/global.json") + + try: + load[str(ctx.guild.id)] + del load[str(ctx.guild.id)] + + with open("./data/json/global.json", "w") as f1: + json.dump(load, f1, ensure_ascii=False, indent=4) + embed = discord.Embed(title="登録を解除しました。", + description="Webhookは手動で削除してください。", + colour=discord.Colour.red()) + await ctx.respond(embed=embed) -@client.on("message") -async def message(message): - if message.source == str(client.user.id): + except KeyError: + webhook_url = await ctx.channel.create_webhook(name="Global") + + load[str(ctx.guild.id)] = {"url": webhook_url.url, "channel": ctx.channel.id} + + with open("./data/json/global.json", "w", encoding="utf-8") as f1: + json.dump(load, f1, ensure_ascii=False, indent=4) + + embed = discord.Embed(title="グローバルチャットに接続しました。", + colour=discord.Colour.green()) + await ctx.respond(embed=embed) + + await asyncio.sleep(5) + + for v in load.values(): + async with aiohttp.botSession() as session: + webhook : Webhook = Webhook.from_url(url = v["url"], session = session) + + await webhook.send(content = f"新しいサーバーが参加しました!\nサーバー名: {ctx.guild.name}", + avatar_url= "https://cdn.discordapp.com/embed/avatars/0.png", + username= "[SYSTEM]") + +@commands.Cog.listener(name="on_ready") +async def ugc_connect(): + await bot.tree.sync() + print("UGCへの接続を開始します...") + await sdk.connect() + +@sdk.on("ready") +async def ready(): + print("Ready for ugc") + +@sdk.on("message") +async def message(message : Message): + if message.source == str(message.author.id): return + if message.author.bot: return - for ch in client.get_all_channels(): - if ch.name == "ugc-test": - embed = discord.Embed(description=message.content, color=0x07cff7) - embed.set_author(name=message.author.name, icon_url=message.author.avatar_url) - await ch.send(embed=embed) - - -""" -@client.on("message") -async def on_ugc_message(message): - if message.where == str(client.user.id): - return - channel = client.get_channel(949862388969119755) - await channel.send(embed=discord.Embed(description=b64encode(dumps(message.data).encode()).decode())) - - -async def recieve_message(message): - if message.author.id == str(client.user.id): - return - if message.channel.id == 949862388969119755: - await client.sdk.request("POST", "/channels", json=loads(b64decode(message.embeds[0].description.encode()).decode())) -""" + load = json_load("data/json/global.json") -@client.event -async def on_message(message): - #await recieve_message(message) - if message.author.bot: + embeds = [] + if message.attachments != []: + if message.attachments[0].width is not None: + embed = discord.Embed(title = "添付ファイル") + embed.set_image(url = message.attachments[0].url) + + embeds.append(embed) + + for k, v in load.items(): + async with aiohttp.botSession() as session: + try: + webhook : Webhook = Webhook.from_url(url = v["url"], session = session) + + except ValueError: + ch = bot.get_channel(v["channel"]) + + webhook = await ch.create_webhook(name="Global") + load[str(message.guild.id)] = {"url": webhook.url, "channel": message.channel.id} + + with open("./data/json/global.json", "w", encoding="utf-8") as f1: + json.dump(load, f1, ensure_ascii=False, indent=4) + + await webhook.send(message.content, + username= message.author.name, + avatar_url= message.author.avatar_url, + embeds = embeds) + + +@commands.Cog.listener(name="on_message") +async def gc_msg(message : discord.Message): + if message.author.bot: #BOTの場合は何もせず終了 return - if message.channel.name != "ugc-test": + + if message.guild is None: return - for ch in client.get_all_channels(): - if message.channel.id == ch.id: - continue - if ch.name == "ugc-test": + + load : dict = json_load("data/json/global.json") + guild_data = load.get(str(message.guild.id)) + + if guild_data is not None: + if message.channel.id == guild_data["channel"]: + if await message_check(message): + return + await message.add_reaction("🔄") - embed = discord.Embed(description=message.content, color=0x07cff7) - embed.set_author(name=message.author.name, icon_url=getattr( - message.author.avatar, "url", None)) - embeds = [embed] - if len(message.attachments) != 0: - for attachment in message.attachments: - e = discord.Embed(color=0x07cff7) - e.set_image(url=attachment.url) - embeds.append(e) - await ch.send(embeds=embeds) - await client.sdk.send(message) - await message.remove_reaction("🔄", client.user) + urls = [] + + for key, value in load.items(): + if key != str(message.guild.id): + urls.append(value['url']) + + embeds = [] + if message.attachments != []: + if message.attachments[0].width is not None: + embed = discord.Embed(title = "添付ファイル") + embed.set_image(url = message.attachments[0].url) + + embeds.append(embed) + + await sdk.send_message(message) + + for url in urls: + async with aiohttp.botSession() as session: + try: + webhook : Webhook = Webhook.from_url(url = url, session = session) + + except ValueError: + webhook = await message.channel.create_webhook(name="Global") + load[str(message.guild.id)] = {"url": webhook.url, "channel": message.channel.id} + + with open("./data/json/global.json", "w", encoding="utf-8") as f1: + json.dump(load, f1, ensure_ascii=False, indent=4) + + await webhook.send(message.content, + username= message.author.name, + avatar_url= message.author.display_avatar.url, + embeds = embeds) + await message.add_reaction("✅") - await asyncio.sleep(3) - await message.remove_reaction("✅", client.user) -client.run(client.config["token"]) +bot.run(bot.config["token"]) diff --git a/sdk/LICENSE b/sdk/LICENSE deleted file mode 100644 index 4f765fe..0000000 --- a/sdk/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Union Global Chat - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. From fccd5882abf28accf7cf45e7ebbdd3113656b19e Mon Sep 17 00:00:00 2001 From: Celely Date: Fri, 28 Apr 2023 20:55:33 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global.json | 1 + main.py | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 global.json diff --git a/global.json b/global.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/global.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/main.py b/main.py index 6539047..9e9c126 100644 --- a/main.py +++ b/main.py @@ -54,13 +54,13 @@ def json_load(path) -> dict: description="グローバルチャットを作成します。すでに作成されている場合はできません。") @app_commands.checks.has_permissions(manage_channels=True) async def gc_join(ctx): - load = json_load("./data/json/global.json") + load = json_load("global.json") try: load[str(ctx.guild.id)] del load[str(ctx.guild.id)] - with open("./data/json/global.json", "w") as f1: + with open("global.json", "w") as f1: json.dump(load, f1, ensure_ascii=False, indent=4) embed = discord.Embed(title="登録を解除しました。", @@ -73,7 +73,7 @@ async def gc_join(ctx): load[str(ctx.guild.id)] = {"url": webhook_url.url, "channel": ctx.channel.id} - with open("./data/json/global.json", "w", encoding="utf-8") as f1: + with open("global.json", "w", encoding="utf-8") as f1: json.dump(load, f1, ensure_ascii=False, indent=4) embed = discord.Embed(title="グローバルチャットに接続しました。", @@ -108,7 +108,7 @@ async def message(message : Message): if message.author.bot: return - load = json_load("data/json/global.json") + load = json_load("global.json") embeds = [] if message.attachments != []: @@ -129,7 +129,7 @@ async def message(message : Message): webhook = await ch.create_webhook(name="Global") load[str(message.guild.id)] = {"url": webhook.url, "channel": message.channel.id} - with open("./data/json/global.json", "w", encoding="utf-8") as f1: + with open("global.json", "w", encoding="utf-8") as f1: json.dump(load, f1, ensure_ascii=False, indent=4) await webhook.send(message.content, @@ -146,7 +146,7 @@ async def gc_msg(message : discord.Message): if message.guild is None: return - load : dict = json_load("data/json/global.json") + load : dict = json_load("global.json") guild_data = load.get(str(message.guild.id)) if guild_data is not None: @@ -180,7 +180,7 @@ async def gc_msg(message : discord.Message): webhook = await message.channel.create_webhook(name="Global") load[str(message.guild.id)] = {"url": webhook.url, "channel": message.channel.id} - with open("./data/json/global.json", "w", encoding="utf-8") as f1: + with open("global.json", "w", encoding="utf-8") as f1: json.dump(load, f1, ensure_ascii=False, indent=4) await webhook.send(message.content, From c101fe9d67be8f399ce7da36df31e9b2d7ac2e1b Mon Sep 17 00:00:00 2001 From: Celely Date: Fri, 28 Apr 2023 23:01:14 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=E6=95=B4=E5=BD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 106 ++++++++++++++++++++++++++++++------------------ sdk/__init__.py | 2 +- sdk/gateway.py | 46 ++++++++++----------- sdk/items.py | 12 +++--- 4 files changed, 97 insertions(+), 69 deletions(-) diff --git a/main.py b/main.py index 9e9c126..0ece780 100644 --- a/main.py +++ b/main.py @@ -7,36 +7,43 @@ import re import aiohttp - bot = commands.Bot(intents=discord.Intents.all()) - sdk = Client("token here") @bot.tree.command(description="Pong") async def ping(interaction): - await interaction.response.send_message("{}ms".format(round(sdk.latency * 1000))) + await interaction.response.send_message("{}ms".format( + round(sdk.latency * 1000))) -invite_pattern = re.compile("(https?://)?((ptb|canary)\.)?(discord\.(gg|io)|discord(app)?.com/invite)/[0-9a-zA-Z]+", re.IGNORECASE) -token_pattern = re.compile("[A-Za-z0-9\-_]{23,30}\.[A-Za-z0-9\-_]{6,7}\.[A-Za-z0-9\-_]{27,40}", re.IGNORECASE) +invite_pattern = re.compile( + "(https?://)?((ptb|canary)\\.)?(discord\\.(gg|io)|discord(app)?.com/invite)/[0-9a-zA-Z]+", + re.IGNORECASE) +token_pattern = re.compile( + "[A-Za-z0-9\\-_]{23,30}\\.[A-Za-z0-9\\-_]{6,7}\\.[A-Za-z0-9\\-_]{27,40}", + re.IGNORECASE) async def message_check(message) -> bool: dis_tok = token_pattern.search(message.content) invite_link = invite_pattern.search(message.content) if dis_tok: - embed = discord.Embed(description = "Discord認証トークンをグローバルチャットに送信することはできません。", colour = discord.Colour.red()) + embed = discord.Embed( + description="Discord認証トークンをグローバルチャットに送信することはできません。", + colour=discord.Colour.red()) await message.channel.send(embed=embed, reference=message) await message.add_reaction('❌') return True elif invite_link: - embed = discord.Embed(description = "Discordの招待リンクをグローバルチャットに送信することはできません。", colour = discord.Colour.red()) + embed = discord.Embed( + description="Discordの招待リンクをグローバルチャットに送信することはできません。", + colour=discord.Colour.red()) - await message.channel.send(embed = embed, reference=message) + await message.channel.send(embed=embed, reference=message) await message.add_reaction('❌') return True @@ -45,13 +52,13 @@ async def message_check(message) -> bool: def json_load(path) -> dict: - with open(path, "r", encoding="utf-8") as f: - load = json.load(f) - return load + with open(path, "r", encoding="utf-8") as f: + load = json.load(f) + return load @bot.tree.command(name="global", - description="グローバルチャットを作成します。すでに作成されている場合はできません。") + description="グローバルチャットを作成します。すでに作成されている場合はできません。") @app_commands.checks.has_permissions(manage_channels=True) async def gc_join(ctx): load = json_load("global.json") @@ -64,31 +71,37 @@ async def gc_join(ctx): json.dump(load, f1, ensure_ascii=False, indent=4) embed = discord.Embed(title="登録を解除しました。", - description="Webhookは手動で削除してください。", - colour=discord.Colour.red()) + description="Webhookは手動で削除してください。", + colour=discord.Colour.red()) await ctx.respond(embed=embed) except KeyError: webhook_url = await ctx.channel.create_webhook(name="Global") - load[str(ctx.guild.id)] = {"url": webhook_url.url, "channel": ctx.channel.id} + load[str(ctx.guild.id)] = { + "url": webhook_url.url, + "channel": ctx.channel.id + } with open("global.json", "w", encoding="utf-8") as f1: json.dump(load, f1, ensure_ascii=False, indent=4) embed = discord.Embed(title="グローバルチャットに接続しました。", - colour=discord.Colour.green()) + colour=discord.Colour.green()) await ctx.respond(embed=embed) await asyncio.sleep(5) for v in load.values(): async with aiohttp.botSession() as session: - webhook : Webhook = Webhook.from_url(url = v["url"], session = session) + webhook: Webhook = Webhook.from_url(url=v["url"], + session=session) + + await webhook.send( + content=f"新しいサーバーが参加しました!\nサーバー名: {ctx.guild.name}", + avatar_url="https://cdn.discordapp.com/embed/avatars/0.png", + username="[SYSTEM]") - await webhook.send(content = f"新しいサーバーが参加しました!\nサーバー名: {ctx.guild.name}", - avatar_url= "https://cdn.discordapp.com/embed/avatars/0.png", - username= "[SYSTEM]") @commands.Cog.listener(name="on_ready") async def ugc_connect(): @@ -96,12 +109,14 @@ async def ugc_connect(): print("UGCへの接続を開始します...") await sdk.connect() + @sdk.on("ready") async def ready(): print("Ready for ugc") + @sdk.on("message") -async def message(message : Message): +async def message(message: Message): if message.source == str(message.author.id): return @@ -113,40 +128,44 @@ async def message(message : Message): embeds = [] if message.attachments != []: if message.attachments[0].width is not None: - embed = discord.Embed(title = "添付ファイル") - embed.set_image(url = message.attachments[0].url) + embed = discord.Embed(title="添付ファイル") + embed.set_image(url=message.attachments[0].url) embeds.append(embed) for k, v in load.items(): async with aiohttp.botSession() as session: try: - webhook : Webhook = Webhook.from_url(url = v["url"], session = session) + webhook: Webhook = Webhook.from_url(url=v["url"], + session=session) except ValueError: ch = bot.get_channel(v["channel"]) webhook = await ch.create_webhook(name="Global") - load[str(message.guild.id)] = {"url": webhook.url, "channel": message.channel.id} + load[str(message.guild.id)] = { + "url": webhook.url, + "channel": message.channel.id + } with open("global.json", "w", encoding="utf-8") as f1: json.dump(load, f1, ensure_ascii=False, indent=4) await webhook.send(message.content, - username= message.author.name, - avatar_url= message.author.avatar_url, - embeds = embeds) + username=message.author.name, + avatar_url=message.author.avatar_url, + embeds=embeds) @commands.Cog.listener(name="on_message") -async def gc_msg(message : discord.Message): - if message.author.bot: #BOTの場合は何もせず終了 +async def gc_msg(message: discord.Message): + if message.author.bot: # BOTの場合は何もせず終了 return if message.guild is None: return - load : dict = json_load("global.json") + load: dict = json_load("global.json") guild_data = load.get(str(message.guild.id)) if guild_data is not None: @@ -164,8 +183,8 @@ async def gc_msg(message : discord.Message): embeds = [] if message.attachments != []: if message.attachments[0].width is not None: - embed = discord.Embed(title = "添付ファイル") - embed.set_image(url = message.attachments[0].url) + embed = discord.Embed(title="添付ファイル") + embed.set_image(url=message.attachments[0].url) embeds.append(embed) @@ -174,20 +193,27 @@ async def gc_msg(message : discord.Message): for url in urls: async with aiohttp.botSession() as session: try: - webhook : Webhook = Webhook.from_url(url = url, session = session) + webhook: Webhook = Webhook.from_url(url=url, + session=session) except ValueError: - webhook = await message.channel.create_webhook(name="Global") - load[str(message.guild.id)] = {"url": webhook.url, "channel": message.channel.id} + webhook = await message.channel.create_webhook( + name="Global") + load[str(message.guild.id)] = { + "url": webhook.url, + "channel": message.channel.id + } with open("global.json", "w", encoding="utf-8") as f1: json.dump(load, f1, ensure_ascii=False, indent=4) - await webhook.send(message.content, - username= message.author.name, - avatar_url= message.author.display_avatar.url, - embeds = embeds) + await webhook.send( + message.content, + username=message.author.name, + avatar_url=message.author.display_avatar.url, + embeds=embeds) await message.add_reaction("✅") + bot.run(bot.config["token"]) diff --git a/sdk/__init__.py b/sdk/__init__.py index 1af9130..cbbd0bf 100644 --- a/sdk/__init__.py +++ b/sdk/__init__.py @@ -1,2 +1,2 @@ from .items import * -from .gateway import Client \ No newline at end of file +from .gateway import Client diff --git a/sdk/gateway.py b/sdk/gateway.py index ce7066a..85468a3 100644 --- a/sdk/gateway.py +++ b/sdk/gateway.py @@ -5,7 +5,6 @@ from websockets.exceptions import ConnectionClosed from .items import Message - import aiohttp try: @@ -14,17 +13,16 @@ import json import zlib - gateway_url = "wss://ugc.renorari.net/api/v2/gateway" message_url = "https://ugc.renorari.net/api/v2/messages" - class Error(Exception): pass class Client: + def __init__(self, token: str): self.token = token self.on_event = {} @@ -41,13 +39,15 @@ async def connect(self): while self.open: await self.recv() - async def request(self, json_data): - json_data["headers"] = {"Authorization": "Bearer {}".format(self.token), - "Content-Type": "application/json"} + json_data["headers"] = { + "Authorization": "Bearer {}".format(self.token), + "Content-Type": "application/json" + } async with aiohttp.ClientSession() as session: - async with session.post(message_url, data = json.dumps(json_data)) as r: + async with session.post(message_url, + data=json.dumps(json_data)) as r: print(r.status) if r.status == 404: raise Error("404エラー") @@ -58,20 +58,21 @@ async def request(self, json_data): elif r.status == 400: raise Error(r.json()["message"]) - - async def send_message(self, message : discord.Message): + async def send_message(self, message: discord.Message): message = json.dumps(self.discord_message_to_ugc_message(message)) - header = {"Authorization": "Bearer {}".format(self.token), - "Content-Type": "application/json"} + header = { + "Authorization": "Bearer {}".format(self.token), + "Content-Type": "application/json" + } async with aiohttp.ClientSession() as session: - async with session.post(message_url, data = message, headers = header) as r: + async with session.post(message_url, data=message, + headers=header) as r: if r.status == 200: return await r.json() else: print(r.status) - @property def open(self) -> bool: return self.ws.open @@ -100,43 +101,42 @@ async def recv(self): if data["success"]: self.dispatch("ready") elif data["type"] == "message": - self.dispatch("message", Message( - data["data"]["data"], data["data"]["source"])) + self.dispatch( + "message", + Message(data["data"]["data"], data["data"]["source"])) elif data["type"] == "heartbeat": self._heartbeat = time.time() - data["data"]["unix_time"] async def identify(self): await self.ws_send("identify", {"token": self.token}) - def on(self, name: str): + def deco(coro): if name in self.on_event: self.on_event[name].append(coro) else: self.on_event[name] = [coro] return coro - return deco + return deco def dispatch(self, name: str, *args): if name in self.on_event: for coro in self.on_event[name]: asyncio.create_task(coro(*args)) - async def ws_send(self, type: str, data: dict): payload = {"type": type, "data": data} await self.ws.send(zlib.compress(json.dumps(payload))) - - def discord_message_to_ugc_message(self, message : discord.Message): + def discord_message_to_ugc_message(self, message: discord.Message): attachments = [] for attachment in message.attachments: attachments_dict = { - "url" : attachment.url, - "name" : attachment.filename, + "url": attachment.url, + "name": attachment.filename, "width": str(attachment.width), "height": str(attachment.height), "content_type": attachment.content_type @@ -173,4 +173,4 @@ def discord_message_to_ugc_message(self, message : discord.Message): } } } - return message_dict \ No newline at end of file + return message_dict diff --git a/sdk/items.py b/sdk/items.py index e517fd4..78a32dd 100644 --- a/sdk/items.py +++ b/sdk/items.py @@ -1,4 +1,5 @@ class Message: + def __init__(self, data: dict, from_: str): self.data = data self.content = data["message"]["content"] @@ -7,15 +8,13 @@ def __init__(self, data: dict, from_: str): self.author = User(data["author"]) self.guild = Guild(data["guild"]) - self.attachments : list[Attachments] = [] + self.attachments: list[Attachments] = [] for a in data["message"]["attachments"]: - self.attachments.append( - Attachments(a) - ) - + self.attachments.append(Attachments(a)) class Attachments: + def __init__(self, data: dict): self.url = data["url"] self.name = data["name"] @@ -25,12 +24,14 @@ def __init__(self, data: dict): class Channel: + def __init__(self, data: dict): self.id = data["id"] self.name = data["name"] class User: + def __init__(self, data: dict): self.name = data["username"] self.id = data["id"] @@ -40,6 +41,7 @@ def __init__(self, data: dict): class Guild: + def __init__(self, data: dict): self.id = data["id"] self.name = data["name"] From 4f6089033b37640a866b3291d50c53d21826b946 Mon Sep 17 00:00:00 2001 From: Celely Date: Sat, 29 Apr 2023 10:19:30 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/main.py b/main.py index 0ece780..52df29d 100644 --- a/main.py +++ b/main.py @@ -7,7 +7,7 @@ import re import aiohttp -bot = commands.Bot(intents=discord.Intents.all()) +bot = commands.Bot(intents=discord.Intents.all(), command_prefix="!.", help_command=None) sdk = Client("token here") @@ -73,7 +73,7 @@ async def gc_join(ctx): embed = discord.Embed(title="登録を解除しました。", description="Webhookは手動で削除してください。", colour=discord.Colour.red()) - await ctx.respond(embed=embed) + await ctx.response.send_message(embed=embed) except KeyError: webhook_url = await ctx.channel.create_webhook(name="Global") @@ -88,12 +88,12 @@ async def gc_join(ctx): embed = discord.Embed(title="グローバルチャットに接続しました。", colour=discord.Colour.green()) - await ctx.respond(embed=embed) + await ctx.response.send_message(embed=embed) await asyncio.sleep(5) for v in load.values(): - async with aiohttp.botSession() as session: + async with aiohttp.ClientSession() as session: webhook: Webhook = Webhook.from_url(url=v["url"], session=session) @@ -103,10 +103,10 @@ async def gc_join(ctx): username="[SYSTEM]") -@commands.Cog.listener(name="on_ready") -async def ugc_connect(): - await bot.tree.sync() +@bot.event +async def on_ready(): print("UGCへの接続を開始します...") + await bot.tree.sync() await sdk.connect() @@ -133,8 +133,8 @@ async def message(message: Message): embeds.append(embed) - for k, v in load.items(): - async with aiohttp.botSession() as session: + for v in load.values(): + async with aiohttp.ClientSession() as session: try: webhook: Webhook = Webhook.from_url(url=v["url"], session=session) @@ -157,8 +157,8 @@ async def message(message: Message): embeds=embeds) -@commands.Cog.listener(name="on_message") -async def gc_msg(message: discord.Message): +@bot.event +async def on_message(message: discord.Message): if message.author.bot: # BOTの場合は何もせず終了 return @@ -216,4 +216,4 @@ async def gc_msg(message: discord.Message): await message.add_reaction("✅") -bot.run(bot.config["token"]) +bot.run("token here")