Skip to content

Commit

Permalink
I might've fixed reconnect issue (still buggy) + slash command
Browse files Browse the repository at this point in the history
  • Loading branch information
Shell1010 committed Jan 13, 2024
1 parent daf15b1 commit 57f7971
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 14 deletions.
2 changes: 1 addition & 1 deletion selfcord/api/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ async def handle_ready_supplemental(self, data: dict):

await self.bot.inbuilt_commands()
await self.bot.emit("ready_supplemental")
await asyncio.sleep(2)
await asyncio.sleep(4)

for guild in self.bot.user.guilds:
if guild.member_count >= 1000:
Expand Down
59 changes: 46 additions & 13 deletions selfcord/api/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
import websockets
from aioconsole import aprint
import ujson

from websockets.client import connect
from websockets.exceptions import ConnectionClosed, ConnectionClosedError, ConnectionClosedOK
if TYPE_CHECKING:
from ..bot import Bot
from websockets import Connect
from websockets import connection
from ..models import Capabilities, Guild, Messageable


Expand Down Expand Up @@ -49,16 +50,36 @@ def __init__(self, bot: Bot, decompress: bool = True) -> None:
"wss://gateway.discord.gg/?encoding=json&v=9"
)

async def linux_run(self, cmd):
proc = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stout, stderr = await proc.communicate()




async def send_json(self, payload: dict):
sleep = 2
if self.ws:
try:
await self.ws.send(ujson.dumps(payload))
except Exception as e:
await aprint(f"Closing because fail send. Attempting reconnect\n{e}")
except ConnectionClosed as e:
if e.rcvd is not None:
if e.rcvd.code == 4008:
sleep += 10
await aprint(f"RECEIVE: {e.rcvd.code} --- {e.rcvd.reason}")
if e.sent is not None:
if e.sent.code == 4008:
sleep += 10
await aprint(f"SENT: {e.sent.code} --- {e.sent.reason}")
await aprint(f"Closing because fail send. Attempting reconnect {self.bot.user.username}\n{e}")
# await self.linux_run(f"notify-send 'RECONNECT HAPPENING NOW CHECK CONSOLE'")
await self.close()
await asyncio.sleep(2)
await self.connect(f"{self.URL}")
await asyncio.sleep(sleep)
await self.connect(f"{self.bot.resume_url}?encoding=json&v=9&compress=zlib-stream")

async def load_async(self, item):
loop = asyncio.get_event_loop()
Expand Down Expand Up @@ -103,14 +124,15 @@ async def recv_json(self):
self.heartbeat_ack()

elif op == self.RECONNECT:
await aprint("Attempting reconnect????")
await aprint(f"Attempting reconnect???? {self.bot.user.username}")
# await self.linux_run(f"notify-send 'RECONNECT HAPPENING NOW CHECK CONSOLE {data} {op}'")
await self.close()
await asyncio.sleep(3)

await self.connect(f"{self.bot.resume_url}?encoding=json&v=9&compress=zlib-stream")

await self.send_json({
"op": 6,
"op": self.RESUME,
"d": {"token": self.token, "session_id": self.bot.session_id, "seq": seq},
})

Expand All @@ -121,7 +143,7 @@ async def recv_json(self):
asyncio.create_task(method(data))

async def connect(self, url: str):
self.ws = await websockets.connect(
self.ws = await connect(
url, origin="https://discord.com", max_size=None,
extra_headers={"user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0"},
read_limit=1000000, max_queue=100, write_limit=1000000,
Expand All @@ -130,17 +152,28 @@ async def connect(self, url: str):
self.zlib = decompressobj(15)

async def start(self, token: str):
sleep = 2
await self.connect(self.URL)

self.token = token
while self.alive:
try:
await self.recv_json()
except Exception as e:
await aprint(f"Closing because fail recv. Attempting reconnect\n{e}")
except ConnectionClosed as e:
if e.rcvd is not None:
if e.rcvd.code == 4008:
sleep += 5

await aprint(f"RECEIVE: {e.rcvd.code} --- {e.rcvd.reason}")
if e.sent is not None:
if e.sent.code == 4008:
sleep += 5
await aprint(f"SENT: {e.sent.code} --- {e.sent.reason}")
await aprint(f"Closing because fail recv. Attempting reconnect {self.bot.user.username}\n{e}")
# await self.linux_run(f"notify-send 'RECONNECT HAPPENING NOW CHECK CONSOLE'")
await self.close()
await asyncio.sleep(2)
await self.connect(f"{self.URL}")
await asyncio.sleep(sleep)
await self.connect(f"{self.bot.resume_url}?encoding=json&v=9&compress=zlib-stream")

async def cache_guild(self, guild: Guild, channel):
payload = {
Expand Down
122 changes: 122 additions & 0 deletions selfcord/models/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import datetime
import time
from .permissions import Permission
import ujson

if TYPE_CHECKING:
from .users import User
from ..bot import Bot
from ..api import HttpClient



class PermissionOverwrite:
def __init__(self, payload: dict, bot: Bot):
self.bot: Bot = bot
Expand All @@ -26,6 +28,83 @@ def update(self, payload: dict):
self.allow: Permission = Permission(payload["allow"], self.bot)
self.deny: Permission = Permission(payload["deny"], self.bot)




class SlashCommand:
def __init__(self, payload: dict, bot: Bot) -> None:
self.bot = bot
self.http = bot.http
self.update(payload)

def update(self, payload):

self.id = payload.get("id")
self.type = payload.get("type")
self.application_id = payload.get("application_id")
self.guild_id = payload.get("guild_id")
self.version = payload.get("version")
self.name = payload.get("name")
self.description = payload.get("description")
self.description_default = payload.get("description_default")
self.options = [SubCommandOption(option, self) for option in payload.get("options", [])]
self.my_options = []
self.integration_types = payload.get("integration_types")
self.raw_data = payload
self.required = payload.get("required", False)

def add_value(self, name: str, value):
new = {}

if type(value) == list:
for option in self.options:
if option.name == name:
new['name'] = name
new['type'] = option.type
new['options'] = value
return self.my_options.append(new)

for option in self.options:
if option.name == name:
new['name'] = name
new['value'] = value
new['type'] = option.type
return self.my_options.append(new)

def get_option(self, name: str):
for option in self.options:
if option.name == name:
return option

class SubCommandOption(SlashCommand):
def __init__(self, payload: dict, main: SlashCommand) -> None:
self.cmd = main
self.update(payload)


def update(self, payload):
self.opt_options = payload.get("options", [])
self.name = payload.get("name")
self.description = payload.get("description")
self.required = payload.get("required", False)
self.type = payload.get("type")
self.my_options = []

def add_value(self, name: str, value):
new = {}
for option in self.opt_options:
if option.get("name", "") == name:
new['name'] = name
new['value'] = value
new['type'] = option['type']
self.my_options.append(new)


def reconstruct(self):
self.cmd.add_value(self.name, self.my_options)



class Channel:
def __init__(self, payload: dict, bot: Bot):
self.bot: Bot = bot
Expand Down Expand Up @@ -113,6 +192,49 @@ async def delayed_delete(self, message, time):
await asyncio.sleep(time)
await message.delete()

async def get_slash_commands(self):
if self.guild_id is not None:
json = await self.http.request(
"GET", f"/guilds/{self.guild_id}/application-command-index"
)
if json is not None:
cmds = []
for cmd in json['application_commands']:
if cmd.get("guild_id") is None:
cmd.update({"guild_id": self.guild_id})
cmds.append(SlashCommand(cmd, self.bot))

return cmds

async def trigger_slash(self, cmd: SlashCommand):
if self.guild_id is not None:
# print(cmd.my_options)

json = {
"type": 2,
"application_id": cmd.application_id,
"guild_id": self.guild_id,
"channel_id": self.id,
"session_id": self.bot.session_id,
"data": {
"version": cmd.version, # version not appending
"id": cmd.id,
"name": cmd.name,
"type": cmd.type,
"options": cmd.my_options,
"application_command": cmd.raw_data
}
}
json = ujson.dumps(json)
boundary = f"----WebkitFormBoundaryNiggerNiggerSexy"
data = f'--{boundary}\r\nContent-Disposition: form-data; name="payload_json"\r\n\r\n{json}\r\n--{boundary}--'

json = await self.http.request(
"POST", "/interactions",
headers={"content-type": f"multipart/form-data; boundary={boundary}"},
data=data
)

async def send(
self, content: str, files: Optional[list[str]] = None, delete_after: Optional[int] = None, tts: bool = False
) -> Optional[Message]:
Expand Down

0 comments on commit 57f7971

Please sign in to comment.