mirror of
https://github.com/hemna/aprsd-telegram-plugin.git
synced 2024-11-26 18:08:39 -05:00
First version working
This commit is contained in:
parent
375037d6a2
commit
754ff73d7e
79
README.rst
79
README.rst
@ -7,43 +7,20 @@ aprsd-telegram-plugin
|
||||
|
||||
|pre-commit|
|
||||
|
||||
.. |PyPI| image:: https://img.shields.io/pypi/v/aprsd-telegram-plugin.svg
|
||||
:target: https://pypi.org/project/aprsd-telegram-plugin/
|
||||
:alt: PyPI
|
||||
.. |Status| image:: https://img.shields.io/pypi/status/aprsd-telegram-plugin.svg
|
||||
:target: https://pypi.org/project/aprsd-telegram-plugin/
|
||||
:alt: Status
|
||||
.. |Python Version| image:: https://img.shields.io/pypi/pyversions/aprsd-telegram-plugin
|
||||
:target: https://pypi.org/project/aprsd-telegram-plugin
|
||||
:alt: Python Version
|
||||
.. |License| image:: https://img.shields.io/pypi/l/aprsd-telegram-plugin
|
||||
:target: https://opensource.org/licenses/MIT
|
||||
:alt: License
|
||||
.. |Read the Docs| image:: https://img.shields.io/readthedocs/aprsd-telegram-plugin/latest.svg?label=Read%20the%20Docs
|
||||
:target: https://aprsd-telegram-plugin.readthedocs.io/
|
||||
:alt: Read the documentation at https://aprsd-telegram-plugin.readthedocs.io/
|
||||
.. |Tests| image:: https://github.com/hemna/aprsd-telegram-plugin/workflows/Tests/badge.svg
|
||||
:target: https://github.com/hemna/aprsd-telegram-plugin/actions?workflow=Tests
|
||||
:alt: Tests
|
||||
.. |Codecov| image:: https://codecov.io/gh/hemna/aprsd-telegram-plugin/branch/main/graph/badge.svg
|
||||
:target: https://codecov.io/gh/hemna/aprsd-telegram-plugin
|
||||
:alt: Codecov
|
||||
.. |pre-commit| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white
|
||||
:target: https://github.com/pre-commit/pre-commit
|
||||
:alt: pre-commit
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* TODO
|
||||
|
||||
* Have a 2 way chat with users of Telegram messenger `http://telegram.org`
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* TODO
|
||||
|
||||
* You have to create a telegram bot and start the bot
|
||||
* Telegram users have to add that bot and then /start
|
||||
* Telegram user can then message the bot
|
||||
* Only after a telegram user has successfully completed the above
|
||||
can you then message a telegram user from an APRS enabled HAM Radio.
|
||||
|
||||
Installation
|
||||
------------
|
||||
@ -55,6 +32,22 @@ You can install *aprsd-telegram-plugin* via pip_ from PyPI_:
|
||||
$ pip install aprsd-telegram-plugin
|
||||
|
||||
|
||||
Now edit your aprsd.yml config file and add the plugin
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
aprsd:
|
||||
enabled_plugins:
|
||||
- aprsd_telegram_plugin.telegram.TelegramChatPlugin
|
||||
|
||||
services:
|
||||
telegram:
|
||||
apiKey: <Your Telegram bot APIkey>
|
||||
shortcuts:
|
||||
'wb': hemna6969
|
||||
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
@ -97,3 +90,31 @@ This project was generated from `@hemna`_'s `APRSD Plugin Python Cookiecutter`_
|
||||
.. github-only
|
||||
.. _Contributor Guide: CONTRIBUTING.rst
|
||||
.. _Usage: https://aprsd-telegram-plugin.readthedocs.io/en/latest/usage.html
|
||||
|
||||
|
||||
.. badges
|
||||
|
||||
.. |PyPI| image:: https://img.shields.io/pypi/v/aprsd-telegram-plugin.svg
|
||||
:target: https://pypi.org/project/aprsd-telegram-plugin/
|
||||
:alt: PyPI
|
||||
.. |Status| image:: https://img.shields.io/pypi/status/aprsd-telegram-plugin.svg
|
||||
:target: https://pypi.org/project/aprsd-telegram-plugin/
|
||||
:alt: Status
|
||||
.. |Python Version| image:: https://img.shields.io/pypi/pyversions/aprsd-telegram-plugin
|
||||
:target: https://pypi.org/project/aprsd-telegram-plugin
|
||||
:alt: Python Version
|
||||
.. |License| image:: https://img.shields.io/pypi/l/aprsd-telegram-plugin
|
||||
:target: https://opensource.org/licenses/MIT
|
||||
:alt: License
|
||||
.. |Read the Docs| image:: https://img.shields.io/readthedocs/aprsd-telegram-plugin/latest.svg?label=Read%20the%20Docs
|
||||
:target: https://aprsd-telegram-plugin.readthedocs.io/
|
||||
:alt: Read the documentation at https://aprsd-telegram-plugin.readthedocs.io/
|
||||
.. |Tests| image:: https://github.com/hemna/aprsd-telegram-plugin/workflows/Tests/badge.svg
|
||||
:target: https://github.com/hemna/aprsd-telegram-plugin/actions?workflow=Tests
|
||||
:alt: Tests
|
||||
.. |Codecov| image:: https://codecov.io/gh/hemna/aprsd-telegram-plugin/branch/main/graph/badge.svg
|
||||
:target: https://codecov.io/gh/hemna/aprsd-telegram-plugin
|
||||
:alt: Codecov
|
||||
.. |pre-commit| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white
|
||||
:target: https://github.com/pre-commit/pre-commit
|
||||
:alt: pre-commit
|
||||
|
@ -1,54 +0,0 @@
|
||||
import logging
|
||||
|
||||
from aprsd import messaging, plugin, trace
|
||||
|
||||
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
class TelegramChatPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
|
||||
version = "1.0"
|
||||
# Look for any command that starts with w or W
|
||||
command_regex = "^[wW]"
|
||||
# the command is for ?
|
||||
command_name = "weather"
|
||||
|
||||
enabled = False
|
||||
|
||||
def setup(self):
|
||||
# Do some checks here?
|
||||
self.enabled = True
|
||||
|
||||
def create_threads(self):
|
||||
"""This allows you to create and return a custom APRSDThread object.
|
||||
|
||||
Create a child of the aprsd.threads.APRSDThread object and return it
|
||||
It will automatically get started.
|
||||
|
||||
You can see an example of one here:
|
||||
https://github.com/craigerl/aprsd/blob/master/aprsd/threads.py#L141
|
||||
"""
|
||||
if self.enabled:
|
||||
# You can create a background APRSDThread object here
|
||||
# Just return it for example:
|
||||
# https://github.com/hemna/aprsd-weewx-plugin/blob/master/aprsd_weewx_plugin/aprsd_weewx_plugin.py#L42-L50
|
||||
#
|
||||
return []
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
|
||||
"""This is called when a received packet matches self.command_regex."""
|
||||
|
||||
LOG.info("TelegramChatPlugin Plugin")
|
||||
|
||||
packet.get("from")
|
||||
packet.get("message_text", None)
|
||||
|
||||
if self.enabled:
|
||||
# Now we can process
|
||||
return "some reply message"
|
||||
else:
|
||||
LOG.warning("TelegramChatPlugin is disabled.")
|
||||
return messaging.NULL_MESSAGE
|
210
aprsd_telegram_plugin/telegram.py
Normal file
210
aprsd_telegram_plugin/telegram.py
Normal file
@ -0,0 +1,210 @@
|
||||
import datetime
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from aprsd import messaging, objectstore, plugin, threads, trace
|
||||
from telegram.ext import Filters, MessageHandler, Updater
|
||||
|
||||
|
||||
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 = {}
|
||||
config = None
|
||||
_shortcuts = {}
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
cls._instance.lock = threading.Lock()
|
||||
cls._instance.config = kwargs["config"]
|
||||
cls._instance.data = {}
|
||||
if kwargs["config"].exists("services.telegram.shortcuts"):
|
||||
cls._instance._shortcuts = kwargs["config"].get("services.telegram.shortcuts")
|
||||
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 = "1.0"
|
||||
# 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 <shortcut/username> <message>",
|
||||
]
|
||||
return _help
|
||||
|
||||
def setup(self):
|
||||
self.enabled = True
|
||||
# Do some checks here?
|
||||
try:
|
||||
self.config.check_option(["services", "telegram", "apiKey"])
|
||||
except Exception as ex:
|
||||
LOG.error(f"Failed to find config telegram:apiKey {ex}")
|
||||
self.enabled = False
|
||||
return
|
||||
|
||||
token = self.config.get("services.telegram.apiKey")
|
||||
|
||||
self.users = TelegramUsers(config=self.config)
|
||||
self.users.load()
|
||||
|
||||
# self.bot = telegram.Bot(token=token)
|
||||
# LOG.info(self.bot.get_me())
|
||||
self.updater = Updater(
|
||||
token=token,
|
||||
use_context=True,
|
||||
persistence=False,
|
||||
)
|
||||
self.dispatcher = self.updater.dispatcher
|
||||
self.dispatcher.add_handler(
|
||||
MessageHandler(
|
||||
Filters.text & (~Filters.command),
|
||||
self.message_handler,
|
||||
),
|
||||
)
|
||||
|
||||
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")
|
||||
tocall = self.config.get("ham.callsign")
|
||||
|
||||
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}")
|
||||
msg = messaging.TextMessage(fromcall, tocall, message)
|
||||
msg.send()
|
||||
elif update.message.chat.type == "group":
|
||||
group_name = "noidea"
|
||||
message = "TelegramGroup({}): {}".format(
|
||||
group_name,
|
||||
update.message.text,
|
||||
)
|
||||
msg = messaging.TextMessage(fromcall, tocall, message)
|
||||
msg.send()
|
||||
|
||||
def create_threads(self):
|
||||
if self.enabled:
|
||||
return TelegramThread(self.config, self.updater)
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
"""This is called when a received packet matches self.command_regex."""
|
||||
LOG.info("TelegramChatPlugin Plugin")
|
||||
|
||||
from_callsign = packet.get("from")
|
||||
message = packet.get("message_text", None)
|
||||
|
||||
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 messaging.NULL_MESSAGE
|
||||
else:
|
||||
LOG.warning("TelegramChatPlugin is disabled.")
|
||||
return messaging.NULL_MESSAGE
|
||||
|
||||
|
||||
class TelegramThread(threads.APRSDThread):
|
||||
def __init__(self, config, updater):
|
||||
super().__init__(self.__class__.__name__)
|
||||
self.config = config
|
||||
self.past = datetime.datetime.now()
|
||||
self.updater = updater
|
||||
|
||||
def stop(self):
|
||||
self.thread_stop = True
|
||||
self.updater.stop()
|
||||
TelegramUsers(config=self.config).save()
|
||||
|
||||
def loop(self):
|
||||
"""We have to loop, so we can stop the thread upon CTRL-C"""
|
||||
self.updater.start_polling(
|
||||
timeout=2,
|
||||
drop_pending_updates=True,
|
||||
)
|
||||
# so we can continue looping
|
||||
return True
|
@ -1,2 +1,3 @@
|
||||
pbr
|
||||
aprsd>=2.2.0
|
||||
aprsd>=2.4.0
|
||||
python-telegram-bot
|
||||
|
Loading…
Reference in New Issue
Block a user