aprsd-telegram-plugin/aprsd_telegram_plugin/telegram.py

238 lines
7.2 KiB
Python
Raw Normal View History

2021-10-25 14:12:33 -04:00
import datetime
import logging
import threading
import time
2021-10-25 14:12:33 -04:00
2022-12-29 15:23:19 -05:00
from aprsd import conf # noqa
from aprsd import objectstore, packets, plugin, threads
2022-12-29 15:23:19 -05:00
from oslo_config import cfg
2021-10-25 14:12:33 -04:00
from telegram.ext import Filters, MessageHandler, Updater
import aprsd_telegram_plugin
2022-12-29 15:23:19 -05:00
from aprsd_telegram_plugin import conf # noqa
2021-10-25 14:12:33 -04:00
2022-12-29 15:23:19 -05:00
CONF = cfg.CONF
2021-10-25 14:12:33 -04:00
LOG = logging.getLogger("APRSD")
class TelegramUsers(objectstore.ObjectStoreMixin):
"""Class to automatically store telegram user ids between starts.
Telegram doesn't provide an API for looking up an userid from
username, so we have to save it off for better user experience.
Unfortunately, we can't get the userid, until the telegram user
sends a message to the bot FIRST.
"""
_instance = None
data = {}
_shortcuts = {}
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.lock = threading.Lock()
cls._instance.data = {}
2022-12-29 15:23:19 -05:00
if CONF.aprsd_telegram_plugin.shortcuts:
cls._instance._shortcuts = CONF.aprsd_telegram_plugin.shortcuts
2021-10-25 14:12:33 -04:00
else:
cls._instance._shortcuts = None
cls._instance._init_store()
return cls._instance
def __getitem__(self, item):
with self.lock:
if item in self._shortcuts:
item = self._shortcuts[item]
return self.data[item]
def __setitem__(self, item, value):
with self.lock:
self.data[item] = value
def __delitem__(self, item):
del self.data[item]
def __contains__(self, item):
if item in self._shortcuts:
item = self._shortcuts[item]
if item in self.data:
return True
else:
return False
def get_shortcuts(self):
return self._shortcuts
class TelegramChatPlugin(plugin.APRSDRegexCommandPluginBase):
version = aprsd_telegram_plugin.__version__
2021-10-25 14:12:33 -04:00
# Look for any command that starts with w or W
command_regex = "^[tT][gG]"
# the command is for ?
command_name = "telegram"
enabled = False
users = None
def help(self):
_help = [
"telegram: Chat with a user on telegram Messenger.",
"telegram: username has to message you first."
"tg: Send tg <username> <message>",
2021-10-25 14:12:33 -04:00
]
return _help
def setup(self):
self.enabled = True
# Do some checks here?
2022-12-29 15:23:19 -05:00
if not CONF.aprsd_telegram_plugin.apiKey:
2021-10-25 14:12:33 -04:00
LOG.error(f"Failed to find config telegram:apiKey {ex}")
self.enabled = False
return
2022-12-29 15:23:19 -05:00
token = CONF.aprsd_telegram_plugin.apiKey
2021-10-25 14:12:33 -04:00
2022-12-29 15:23:19 -05:00
self.users = TelegramUsers()
2021-10-25 14:12:33 -04:00
self.users.load()
# self.bot = telegram.Bot(token=token)
# LOG.info(self.bot.get_me())
2022-12-02 14:00:17 -05:00
LOG.info("Starting up Updater")
try:
self.updater = Updater(
token=token,
use_context=True,
persistence=False,
)
except Exception as ex:
self.enabled = False
LOG.exception(ex)
LOG.info("Starting up Dispatcher")
try:
self.dispatcher = self.updater.dispatcher
self.dispatcher.add_handler(
MessageHandler(
Filters.text & (~Filters.command),
self.message_handler,
),
)
except Exception as ex:
self.enabled = False
LOG.exception(ex)
2021-10-25 14:12:33 -04:00
def message_handler(self, update, context):
"""This is called when a telegram users texts the bot."""
LOG.info(f"{self.__class__.__name__}: Got message {update.message.text}")
# LOG.info(f"Text {update.message.text}")
# LOG.info(f"Chat {update.message.chat}")
# LOG.info(f"From {update.message.from.username} : ")
fromcall = self.config.get("aprs.login")
2022-12-29 15:23:19 -05:00
tocall = CONF.callsign
2021-10-25 14:12:33 -04:00
if update.message.chat.type == "private":
LOG.info(f"Username {update.message.chat.username} - ID {update.message.chat.id}")
message = "Telegram({}): {}".format(
update.message.chat.username,
update.message.text,
)
self.users[update.message.chat.username] = update.message.chat.id
# LOG.debug(self.users)
# LOG.info(f"{message}")
pkt = packets.MessagePacket(
from_call=fromcall,
to_call=tocall,
message_text=message,
)
pkt.send()
2021-10-25 14:12:33 -04:00
elif update.message.chat.type == "group":
group_name = "noidea"
message = "TelegramGroup({}): {}".format(
group_name,
update.message.text,
)
pkt = packets.MessagePacket(
from_call=fromcall,
to_call=tocall,
message_text=message,
)
pkt.send()
2021-10-25 14:12:33 -04:00
def create_threads(self):
if self.enabled:
2022-12-02 14:00:17 -05:00
LOG.info("Starting TelegramThread")
2022-12-29 15:23:19 -05:00
return TelegramThread(self.updater)
2021-10-25 14:12:33 -04:00
def process(self, packet):
"""This is called when a received packet matches self.command_regex."""
LOG.info("TelegramChatPlugin Plugin")
from_callsign = packet.from_call
message = packet.message_text
2021-10-25 14:12:33 -04:00
if self.enabled:
# Now we can process
# Only allow aprsd owner to use this.
mycall = self.config["ham"]["callsign"]
# Only allow the owner of aprsd to send a tweet
if not from_callsign.startswith(mycall):
return "Unauthorized"
# Always should have format of
# <command> <username> <message>
parts = message.split(" ")
LOG.info(parts)
if len(parts) < 3:
return "invalid request"
# parts[0] is the command
username = parts[1]
msg = " ".join(parts[2:])
if username not in self.users:
# Unfortunately there is no way to lookup a user ID
# from a username right now.
return f"Need a message from {username} first"
bot = self.updater.bot
bot.sendMessage(
chat_id=self.users[username],
text=msg,
)
return packets.NULL_MESSAGE
2021-10-25 14:12:33 -04:00
else:
LOG.warning("TelegramChatPlugin is disabled.")
return packets.NULL_MESSAGE
2021-10-25 14:12:33 -04:00
class TelegramThread(threads.APRSDThread):
2022-12-29 15:23:19 -05:00
def __init__(self, updater):
2021-10-25 14:12:33 -04:00
super().__init__(self.__class__.__name__)
self.past = datetime.datetime.now()
self.updater = updater
def stop(self):
self.thread_stop = True
self.updater.stop()
2022-12-29 15:23:19 -05:00
TelegramUsers().save()
2021-10-25 14:12:33 -04:00
def loop(self):
"""We have to loop, so we can stop the thread upon CTRL-C"""
2022-12-02 14:00:17 -05:00
try:
self.updater.start_polling(
timeout=2,
drop_pending_updates=True,
)
except Exception as ex:
LOG.exception(ex)
return False
# So we don't eat 100% CPU
time.sleep(1)
2021-10-25 14:12:33 -04:00
# so we can continue looping
return True