mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 19:44:20 +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