diff --git a/chatlib.py b/chatlib.py index 1c426eb..78876f1 100644 --- a/chatlib.py +++ b/chatlib.py @@ -3,7 +3,8 @@ import time import asyncio import datetime import traceback -from .utils import Event,Listener +from dataclasses import dataclass +from asyncio import iscoroutinefunction func=type(lambda:1) # Gross, but I can't actually find it. # And yes, lamdas are too. @@ -12,26 +13,12 @@ tracing=False def trace(*msg): if tracing: print(datetime.datetime.now(),*msg) -class MatrixClient(): +class ChatClient(): def __init__(self,homeserver,token): self.event_queue=asyncio.Queue() #Contains Events. Everything in here must be an Event. self.listeners=[] self.timers=[] - async def request(self,endpoint='sync',method='GET', ver=0,headers={} *args,**kwargs): - async with self.session.request(method, f'{self.baseurl}/r{ver}/{endpoint}', headers=headers|{'Authorization':f'Bearer {self.token}'}, *args,**kwargs) as fetched: - if fetched.status_code!=200: raise Exception('fix ur shit') - try: return await fetched.json() - except JSONDecodeError: pass # TODO: Figure out what this is called - - # This function just dumps /sync blobs in the event queue as a raw event. - # ALL handling is deferred to handlers. - # ... Except updating the since token. That's important to do here to guarantee that it never calls with the same token twice. - async def sync(self): - blob=await self.request(params={'timeout':30000,'since':self.since}) - self.since=blob['next_batch'] - self.event_queue.put(Event('m.sync',None,blob)) - def addlistener(self,name=None,match=None): if isinstance(name,func) and match==None: match=name; name=None def __wrap__(funky): @@ -55,4 +42,29 @@ class MatrixClient(): for listener in self.listeners: if listener==item: await listener(item) - \ No newline at end of file + +@dataclass +class Event(): + event_type:str + data:Object=None + raw_data:dict=None + outbound:bool=False + +class Listener(): + def __init__(self,name,match,function): + self.name=name or function.__name__ + self.match=match + self._matchstr=isinstance(match,str) + self.function=function + self._async=iscoroutinefunction(function) + + async def __call__(self,*args,**kwargs): + if not self._async: return self.function(*args,**kwargs) + return await self.function(*args,**kwargs) + + def __eq__(self,other): + if isinstance(other,Event): + if self._matchstr: return self.match==other.event_type + return self.match(other) # If it's not a string, assume it's a callable. + else: return super.__eq__(self,other) + def __str__(self): return self.name \ No newline at end of file diff --git a/listeners.py b/listeners.py index dbaccf5..3cd6227 100644 --- a/listeners.py +++ b/listeners.py @@ -1,5 +1,6 @@ -from .utils import Listener as _l, Event as _e, get_or_create as goc +from .utils import get_or_create as goc import .models +from .chatlib import Listener as _l, Event as _e # Default handlers will be given two parts each: # 1) Mould the raw event into a parsed event (update internal state) diff --git a/matrixapi.py b/matrixapi.py new file mode 100644 index 0000000..2424fc9 --- /dev/null +++ b/matrixapi.py @@ -0,0 +1,5 @@ +async def request(self,endpoint='sync',method='GET', ver=0,headers={} *args,**kwargs): + async with self.session.request(method, f'{self.baseurl}/r{ver}/{endpoint}', headers=headers|{'Authorization':f'Bearer {self.token}'}, *args,**kwargs) as fetched: + if fetched.status_code!=200: raise Exception('fix ur shit') + try: return await fetched.json() + except JSONDecodeError: pass # TODO: Figure out what this is called \ No newline at end of file diff --git a/matrixbot.py b/matrixbot.py index 75b515f..ee35ba3 100644 --- a/matrixbot.py +++ b/matrixbot.py @@ -1,4 +1,4 @@ -from .matrix import * +from .chatlib import * import .listeners real_listeners=dict(filter(lambda x:isinstance(x,Listener),listeners.__dict__.items())) diff --git a/models.py b/models.py index 796dd04..0659d07 100644 --- a/models.py +++ b/models.py @@ -1,6 +1,6 @@ -from .utils import redc +from dataclasses import dataclass, field -@redc +@dataclass class Account(): mxid:str username:str @@ -9,7 +9,7 @@ class Account(): nickname:str='' rooms:List[Room]=field(default_factory=list) -@redc +@dataclass class Room(): id:str name:str='' @@ -17,23 +17,23 @@ class Room(): parents:List[Space]=field(default_factory=list) messages:List[Message]=field(default_factory=list) -@redc +@dataclass class Space(Room): children:List[Room]=field(default_factory=list) -@redc +@dataclass class Message(): id:str contents:str author:Member -@redc +@dataclass class User(): mxid:str nick:str avatar:str -@redc +@dataclass class Member(User): room_av:str room_nick:str \ No newline at end of file diff --git a/timers.py b/timers.py new file mode 100644 index 0000000..f4f112d --- /dev/null +++ b/timers.py @@ -0,0 +1,7 @@ +# This function just dumps /sync blobs in the event queue as a raw event. +# ALL handling is deferred to handlers. +# ... Except updating the since token. That's important to do here to guarantee that it never calls with the same token twice. +async def sync(self): + blob=await self.request(params={'timeout':30000,'since':self.since}) + self.since=blob['next_batch'] + self.event_queue.put(Event('m.sync',None,blob)) \ No newline at end of file diff --git a/utils.py b/utils.py index 540e007..81f9c85 100644 --- a/utils.py +++ b/utils.py @@ -1,32 +1,3 @@ -from dataclasses import dataclass, field, _MISSING_TYPE as mt -from asyncio import iscoroutinefunction - -@dataclass -class Event(): - event_type:str - data:Object=None - raw_data:dict=None - outbound:bool=False - -class Listener(): - def __init__(self,name,match,function): - self.name=name or function.__name__ - self.match=match - self._matchstr=isinstance(match,str) - self.function=function - self._async=iscoroutinefunction(function) - - async def __call__(self,*args,**kwargs): - if not self._async: return self.function(*args,**kwargs) - return await self.function(*args,**kwargs) - - def __eq__(self,other): - if isinstance(other,Event): - if self._matchstr: return self.match==other.event_type - return self.match(other) # If it's not a string, assume it's a callable. - else: return super.__eq__(self,other) - def __str__(self): return self.name - def get_or_create(needle,haystack,default): """ This is a wrapper for filter that can add stuff. Nothing special. Needle is a function, default isn't. Haystack is a list. I might fix that later.