mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 11:34:18 +00:00
First commit!
This commit is contained in:
commit
06e79562f8
11 changed files with 125 additions and 0 deletions
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
|||
python-telegram-bot>=11.1.0
|
0
royalnet/__init__.py
Normal file
0
royalnet/__init__.py
Normal file
0
royalnet/bots/__init__.py
Normal file
0
royalnet/bots/__init__.py
Normal file
61
royalnet/bots/telegram.py
Normal file
61
royalnet/bots/telegram.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
import telegram
|
||||
import asyncio
|
||||
import typing
|
||||
from ..commands import PingCommand
|
||||
from ..utils import asyncify, Call
|
||||
|
||||
|
||||
class TelegramBot:
|
||||
def __init__(self, api_key: str):
|
||||
self.bot = telegram.Bot(api_key)
|
||||
self.should_run = False
|
||||
self.offset = -100
|
||||
self.commands = {
|
||||
"/ping": PingCommand
|
||||
}
|
||||
|
||||
class TelegramCall(Call):
|
||||
interface_name = "telegram"
|
||||
interface_obj = self
|
||||
|
||||
async def reply(call, text: str):
|
||||
await asyncify(call.channel.send_message, text, parse_mode="HTML")
|
||||
self.Call = TelegramCall
|
||||
|
||||
async def run(self):
|
||||
self.should_run = True
|
||||
while self.should_run:
|
||||
# Get the latest 100 updates
|
||||
last_updates: typing.List[telegram.Update] = await asyncify(self.bot.get_updates, offset=self.offset)
|
||||
# Handle updates
|
||||
for update in last_updates:
|
||||
# noinspection PyAsyncCall
|
||||
asyncio.create_task(self.handle_update(update))
|
||||
# Recalculate offset
|
||||
try:
|
||||
self.offset = last_updates[-1].update_id + 1
|
||||
except IndexError:
|
||||
pass
|
||||
# Wait for a while TODO: use long polling
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def handle_update(self, update: telegram.Update):
|
||||
# Skip non-message updates
|
||||
if update.message is None:
|
||||
return
|
||||
message: telegram.Message = update.message
|
||||
# Skip no-text messages
|
||||
if message.text is None:
|
||||
return
|
||||
text: str = message.text
|
||||
# Find and clean parameters
|
||||
command_text, *parameters = text.split(" ")
|
||||
command_text.replace(f"@{self.bot.username}", "")
|
||||
# Find the function
|
||||
try:
|
||||
command = self.commands[command_text]
|
||||
except KeyError:
|
||||
# Skip inexistent commands
|
||||
return
|
||||
# Call the command
|
||||
return await self.Call(message.chat, command).run()
|
3
royalnet/commands/__init__.py
Normal file
3
royalnet/commands/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from .ping import PingCommand
|
||||
|
||||
__all__ = ["PingCommand"]
|
6
royalnet/commands/ping.py
Normal file
6
royalnet/commands/ping.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from ..utils import Command, Call
|
||||
|
||||
|
||||
class PingCommand(Command):
|
||||
async def common(self, call: Call, *args, **kwargs):
|
||||
await call.reply("Pong!")
|
5
royalnet/utils/__init__.py
Normal file
5
royalnet/utils/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from .asyncify import asyncify
|
||||
from .call import Call
|
||||
from .command import Command
|
||||
|
||||
__all__ = ["asyncify", "Call", "Command"]
|
8
royalnet/utils/asyncify.py
Normal file
8
royalnet/utils/asyncify.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
import asyncio
|
||||
import functools
|
||||
import typing
|
||||
|
||||
|
||||
async def asyncify(function: typing.Callable, *args, **kwargs):
|
||||
loop = asyncio.get_running_loop()
|
||||
return await loop.run_in_executor(None, functools.partial(function, *args, **kwargs))
|
25
royalnet/utils/call.py
Normal file
25
royalnet/utils/call.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from .command import Command
|
||||
|
||||
|
||||
class Call:
|
||||
"""A command call. Still an abstract class, subbots should create a new call from this."""
|
||||
|
||||
# These parameters / methods should be overridden
|
||||
interface_name = NotImplemented
|
||||
interface_obj = NotImplemented
|
||||
|
||||
async def reply(cls, text: str):
|
||||
"""Send a text message to the channel the call was made."""
|
||||
raise NotImplementedError()
|
||||
|
||||
# These parameters / methods should be left alone
|
||||
def __init__(self, channel, command: Command):
|
||||
self.channel = channel
|
||||
self.command = command
|
||||
|
||||
async def run(self, *args, **kwargs):
|
||||
try:
|
||||
coroutine = getattr(self.command, self.interface_name)
|
||||
except AttributeError:
|
||||
coroutine = getattr(self.command, "common")
|
||||
return await coroutine(self.command, self, *args, **kwargs)
|
10
royalnet/utils/command.py
Normal file
10
royalnet/utils/command.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
import typing
|
||||
if typing.TYPE_CHECKING:
|
||||
from .call import Call
|
||||
|
||||
|
||||
class Command:
|
||||
"""A generic command, called from any source."""
|
||||
|
||||
async def common(self, call: "Call", *args, **kwargs):
|
||||
raise NotImplementedError()
|
6
test.py
Normal file
6
test.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
import asyncio
|
||||
from royalnet.bots.telegram import TelegramBot
|
||||
|
||||
bot = TelegramBot("375232708:AAExsD_prmxJOXzmJwYZyNUt5zc_EbXxR38")
|
||||
|
||||
asyncio.get_event_loop().run_until_complete(bot.run())
|
Loading…
Reference in a new issue