parent
9139adef70
commit
459496d198
@ -1,2 +1,3 @@ |
||||
*.pyc |
||||
__pycache__ |
||||
.vscode |
@ -1,2 +1,16 @@ |
||||
# modular-discord-bot-fw |
||||
framework for discord bots that uses contained files for individual feature plugins that can be modified and reloaded on the fly |
||||
|
||||
basic structure: |
||||
* client class has implementations for handling opcodes |
||||
* event response is up to the client (!!) |
||||
* this lets us expose events for plugins to hook into, but keeps core API interaction defined in-class |
||||
* any and all bot features should be useable via independent feature plugins |
||||
* these are dynamically found and run, so they can be hotloaded, modified, etc. without disconnecting or restarting the whole bot session |
||||
|
||||
todo before it's basically finished: |
||||
* basic rate limiting |
||||
* plugin manager & hook integration |
||||
* make sure broken sockets are properly addressed with a resume or reconnect |
||||
* default plugins so the bot does _something_ out of the box |
||||
* plugin reloading, user (command) authentication, etc. will also probably be written in plugin form |
@ -0,0 +1,48 @@ |
||||
import websockets, asyncio, json |
||||
from random import randint |
||||
import disc_api |
||||
from grim_logger import glog, glog_level |
||||
class client(): |
||||
def __init__(self, token): |
||||
self.logger = glog(level = glog_level.TRACE) |
||||
self.ws = None |
||||
self.last_sequence = None |
||||
self.session_id = None |
||||
self.token = token |
||||
self.user = None |
||||
async def shit(self): |
||||
# https://discord.com/developers/docs/topics/gateway#connecting-to-the-gateway |
||||
# you're supposed to ask an HTTP endpoint what the ws gateway is but the docs didnt tell me the domain after 1sec so HAHA NOPE |
||||
async with websockets.connect("wss://gateway.discord.gg/?encoding=json&v=9") as self.ws:# they seem to be fond of making new versions randomly and still supporting old ones? |
||||
while True: # they may or may not drop support for this soon # nevermind we're on v9 now |
||||
data = json.loads(await self.ws.recv()) |
||||
handler = getattr(self, "op_"+disc_api.gateway_opcodes[data["op"]], None) |
||||
self.logger.write(data) |
||||
self.last_sequence = data["s"] |
||||
if handler: |
||||
await handler(data) |
||||
async def heartbeat(self, interval): |
||||
while True: |
||||
self.logger.write(interval/1000) |
||||
await self.ws.send(json.dumps({"op":1, "d":self.last_sequence})) |
||||
await asyncio.sleep(interval/1000) |
||||
self.logger.write("sent heartbeat") |
||||
async def op_hello(self, res): |
||||
if not(self.last_sequence and self.session_id): |
||||
interval = res["d"]["heartbeat_interval"] |
||||
asyncio.get_event_loop().create_task(self.heartbeat(interval)) |
||||
await asyncio.sleep(1) |
||||
# https://discord.com/developers/docs/topics/gateway#identify-example-identify |
||||
await self.ws.send(json.dumps({"op":2, "d":{"token":self.token, "intents": 513, "properties":{"$os":"linux", "browser":"mdbf", "device":"mdbf"}}})) |
||||
else: |
||||
# https://discord.com/developers/docs/topics/gateway#identify-example-identify |
||||
await self.ws.send(json.dumps({"op":6, "d":{"token":self.token, "session_id":self.session_id, "seq":self.last_sequence}})) |
||||
async def op_invalid_session(self, res): |
||||
await asyncio.sleep(randint(1, 5)) # try again |
||||
await self.ws.send(json.dumps({"op":2, "d":{"token":self.token, "intents": 513, "properties":{"$os":"linux", "browser":"mdbf", "device":"mdbf"}}})) |
||||
async def op_dispatch(self, res): |
||||
if res["t"] == "READY": |
||||
self.session_id = res["d"]["session_id"] |
||||
self.user = res["d"]["user"] |
||||
def run(self): |
||||
asyncio.run(self.shit()) |
@ -0,0 +1,26 @@ |
||||
from enum import Enum |
||||
from inspect import stack |
||||
from sys import stdout |
||||
from datetime import datetime |
||||
class glog_flags(Enum): |
||||
METHOD_PRINT = 0 |
||||
METHOD_FILE = 1 |
||||
class glog_level(Enum): |
||||
PLAIN = 0 |
||||
TRACE = 1 |
||||
class glog(): |
||||
def __init__(self, flags = [glog_flags.METHOD_PRINT], filename = "bot.log", level = glog_level.PLAIN): |
||||
if not flags: raise ValueError("no glog_flags given!") |
||||
self.targets = [] |
||||
if glog_flags.METHOD_PRINT in flags: |
||||
self.targets.append(stdout) |
||||
if glog_flags.METHOD_FILE in flags: |
||||
self.targets.append(open(filename, "a")) |
||||
self.level = level |
||||
def __del__(self): |
||||
for target in self.targets: |
||||
if not(target == stdout): |
||||
target.close() |
||||
def write(self, message): |
||||
for target in self.targets: |
||||
target.write("({2})@[{0}]: {1}\n".format(stack()[1].function if self.level == glog_level.TRACE else "glog", message, datetime.now())) |
@ -1,15 +1,3 @@ |
||||
import websockets, asyncio, json |
||||
import disc_api |
||||
class client(): |
||||
def __init__(self): |
||||
pass |
||||
async def shit(): |
||||
# https://discord.com/developers/docs/topics/gateway#connecting-to-the-gateway |
||||
# you're supposed to ask an HTTP endpoint what the ws gateway is but the docs didnt tell me the domain after 1sec so HAHA NOPE |
||||
async with websockets.connect("wss://gateway.discord.gg/?encoding=json&v=9") as ws: # they seem to be fond of making new versions randomly and still supporting old ones? |
||||
while True: # they may or may not drop support for this soon |
||||
data = (await ws.recv()) |
||||
print(data) |
||||
break |
||||
def run(): |
||||
asyncio.run(shit()) |
||||
from client import client |
||||
olive = client("OTIxMDg0ODA3Mjk4MDU2MjAz.YbtxEg.NxS1h63qKDeTn_4voc2Axa_QIFI") |
||||
olive.run() |
Loading…
Reference in new issue