initial commit
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
commit
c22dade3d5
21 files changed
+564
No files matched your search
Whitespace-only changes.
+21
@@ -0,0 +1,21 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from core.text import split
|
||||
NOTE="-# slow down, this rep was rate limited, if you still spam the rate limite will increase"
|
||||
class Ctx(commands.Context):
|
||||
limited=False
|
||||
async def send(s,content=None,**kw):
|
||||
kw.setdefault("mention_author",False)
|
||||
text=None if content is None else str(content)
|
||||
if s.limited:
|
||||
text=f"{text}\n{NOTE}" if text else NOTE
|
||||
parts=split(text,1900) if len(text)>2000 else [text]
|
||||
else:parts=split(text) if text is not None and len(text)>2000 else [text]
|
||||
m=None
|
||||
for i,p in enumerate(parts):
|
||||
k=kw if i==len(parts)-1 else {"mention_author":False}
|
||||
try:m=await s.message.reply(p,**k)
|
||||
except discord.HTTPException:
|
||||
k.pop("mention_author",None);m=await s.channel.send(p,**k)
|
||||
return m
|
||||
reply=send
|
||||
@@ -0,0 +1,28 @@
|
||||
import asyncio,time,aiohttp
|
||||
from core.text import split
|
||||
class Hook:
|
||||
def __init__(s,url):
|
||||
s.url=url;s.q=asyncio.Queue();s.task=None;s.http=None
|
||||
async def start(s):
|
||||
s.http=aiohttp.ClientSession();s.task=asyncio.create_task(s.run())
|
||||
def put(s,text):
|
||||
line=f"`{time.strftime('%d/%m %H:%M:%S')}` {text}"
|
||||
print(line)
|
||||
if s.url:
|
||||
for c in split(line,1900):s.q.put_nowait(c)
|
||||
async def run(s):
|
||||
while True:
|
||||
item=await s.q.get()
|
||||
for i in range(6):
|
||||
try:
|
||||
async with s.http.post(s.url,json={"content":item,"allowed_mentions":{"parse":[]}}) as r:
|
||||
if r.status==429:
|
||||
await asyncio.sleep(float((await r.json()).get("retry_after",1))+.1);continue
|
||||
if r.status<500:break
|
||||
await asyncio.sleep(1+i)
|
||||
except Exception:await asyncio.sleep(1+i)
|
||||
s.q.task_done();await asyncio.sleep(.25)
|
||||
async def close(s):
|
||||
try:await asyncio.wait_for(s.q.join(),15)
|
||||
except asyncio.TimeoutError:pass
|
||||
s.task.cancel();await s.http.close()
|
||||
@@ -0,0 +1,26 @@
|
||||
import asyncio,time
|
||||
BASE,MAX,STEP,CALM,CAP=0.5,2.0,0.25,5.0,20
|
||||
class State:
|
||||
__slots__=("slot","interval","last","tasks")
|
||||
def __init__(s):s.slot=0.0;s.interval=BASE;s.last=0.0;s.tasks=set()
|
||||
class Limiter:
|
||||
def __init__(s):s.users={}
|
||||
def admit(s,uid):
|
||||
now=time.monotonic();st=s.users.get(uid)
|
||||
if st is None:st=s.users[uid]=State()
|
||||
if len(st.tasks)>=CAP:return st,None,True
|
||||
if now-st.last>CALM:st.interval=BASE
|
||||
wait=max(0.0,st.slot-now)
|
||||
if wait>0:st.interval=min(MAX,st.interval+STEP)
|
||||
st.slot=max(now,st.slot)+st.interval;st.last=now
|
||||
return st,wait,False
|
||||
def flush(s,st):
|
||||
n=len(st.tasks)
|
||||
for t in list(st.tasks):t.cancel()
|
||||
st.tasks.clear();st.slot=0.0;st.interval=BASE;return n
|
||||
def track(s,uid,st,task):
|
||||
st.tasks.add(task)
|
||||
def done(t):
|
||||
st.tasks.discard(t)
|
||||
if not st.tasks and time.monotonic()-st.last>CALM*2 and s.users.get(uid) is st:s.users.pop(uid,None)
|
||||
task.add_done_callback(done)
|
||||
@@ -0,0 +1,8 @@
|
||||
from pathlib import Path
|
||||
ROOT=Path(__file__).resolve().parent.parent
|
||||
async def load_all(bot):
|
||||
for d in ("cmds","events"):
|
||||
for f in sorted((ROOT/d).glob("*.py")):
|
||||
if f.name.startswith("_"):continue
|
||||
try:await bot.load_extension(f"{d}.{f.stem}")
|
||||
except Exception as e:bot.hook.put(f"module load failed: {d}.{f.stem}: {e!r}")
|
||||
@@ -0,0 +1,10 @@
|
||||
import asyncio,random,time
|
||||
async def presence(bot,interval=45):
|
||||
await bot.wait_until_ready()
|
||||
since=int(time.time()*1000)
|
||||
while True:
|
||||
payload={"op":3,"d":{"status":"idle","since":since,"afk":True,"activities":[]}}
|
||||
try:
|
||||
if bot.ws:await bot.ws.send_as_json(payload)
|
||||
except Exception as e:bot.hook.put(f"presence update failed: {e!r}")
|
||||
await asyncio.sleep(interval+random.uniform(-5,5))
|
||||
@@ -0,0 +1,10 @@
|
||||
def split(t,n=1990):
|
||||
out=[];op=False
|
||||
while t:
|
||||
pre="```\n" if op else "";room=n-len(pre)-4
|
||||
if len(t)<=room:out.append(pre+t);break
|
||||
cut=t.rfind("\n",0,room);cut=cut if cut>room//2 else room
|
||||
chunk=t[:cut];t=t[cut:].lstrip("\n")
|
||||
o=op^(chunk.count("```")%2==1)
|
||||
out.append(pre+chunk+("\n```" if o else ""));op=o
|
||||
return out or [""]
|
||||
@@ -0,0 +1,22 @@
|
||||
import asyncio
|
||||
import functools
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
_pool = ThreadPoolExecutor(max_workers=32, thread_name_prefix="dfu")
|
||||
async def run_blocking(func, /, *args, **kwargs):
|
||||
loop = asyncio.get_running_loop()
|
||||
return await loop.run_in_executor(_pool, functools.partial(func, *args, **kwargs))
|
||||
def threaded(func):
|
||||
@functools.wraps(func)
|
||||
async def wrapper(*args, **kwargs):
|
||||
return await run_blocking(func, *args, **kwargs)
|
||||
return wrapper
|
||||
def spawn(coro, *, name=None):
|
||||
task = asyncio.create_task(coro, name=name)
|
||||
def _done(t):
|
||||
if not t.cancelled() and t.exception():
|
||||
print(f"task {name or ''} error: {t.exception()!r}")
|
||||
|
||||
task.add_done_callback(_done)
|
||||
return task
|
||||
def shutdown():
|
||||
_pool.shutdown(wait=False, cancel_futures=True)
|
||||
@@ -0,0 +1,35 @@
|
||||
import asyncio,json,os
|
||||
from pathlib import Path
|
||||
from core.threads import run_blocking
|
||||
def _read(p):
|
||||
if not p.exists():p.write_text("[]")
|
||||
return p.stat().st_mtime_ns,frozenset(int(x) for x in json.loads(p.read_text()))
|
||||
def _write(p,ids):
|
||||
t=p.with_suffix(".tmp");t.write_text(json.dumps(sorted(ids),indent=2));os.replace(t,p);return p.stat().st_mtime_ns
|
||||
class Whitelist:
|
||||
def __init__(s,path,hook):
|
||||
s.path=Path(path);s.hook=hook;s.ids=frozenset();s.m=None;s.lock=asyncio.Lock();s.task=None
|
||||
def __contains__(s,i):return i in s.ids
|
||||
async def load(s):
|
||||
try:m,ids=await run_blocking(_read,s.path)
|
||||
except Exception as e:
|
||||
s.hook.put(f"whitelist unreadable: {e!r}, keeping previous list")
|
||||
try:s.m=(await run_blocking(s.path.stat)).st_mtime_ns
|
||||
except OSError:pass
|
||||
return
|
||||
if s.m is not None and ids!=s.ids:s.hook.put(f"whitelist reloaded ({len(ids)} ids)")
|
||||
s.ids=ids;s.m=m
|
||||
async def watch(s):
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
try:m=(await run_blocking(s.path.stat)).st_mtime_ns
|
||||
except OSError:m=None
|
||||
if m!=s.m:await s.load()
|
||||
async def start(s):
|
||||
await s.load();s.task=asyncio.create_task(s.watch())
|
||||
async def mutate(s,i,add):
|
||||
async with s.lock:
|
||||
n=set(s.ids)
|
||||
if add:n.add(i)
|
||||
else:n.discard(i)
|
||||
s.m=await run_blocking(_write,s.path,n);s.ids=frozenset(n)
|
||||
Reference in new issue
Block a user