Commit 80b76d1f authored by Otthorn's avatar Otthorn 🤔

Initial commit

parents
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Environments
envs
envs2
envs3
# Eclipse
.project
.pydevproject
# PyCharm
.idea
# OS files
.DS_Store
*.swp
# Senible data storage
*.env
This diff is collapsed.
# med-maid
## Description
Un bot matrix pour les channels de la med.
## Requirement
basé sur [python-matrix-bot](https://github.com/shawnanastasio/python-matrix-bot-api),
Qui lui même requiert [matrix-python-sdk](https://github.com/matrix-org/matrix-python-sdk).
Il faut également un compte matrix et une clé API pour giphy (ben oui je ne fourni pas ouvertement celui de med-maid :p).
## Lancer le bot
```python med-maid.py```
# TODO list:
[x] fonction !gif avec l'API python giphy
[] fonction !meteo avec weather-api
[] fonction !music pour changer la musique à la med
[] fonction qui renvoie les menus pour les commandes de nourritures à la med
[x] fichiers .env avec les identifiants et les clés API
[x] fichiers .env dans le gitignore
[] fichiers .env.example pour expliquer la syntaxe du fichier
[] faire un repo sur le gitlab du crans
[] module pour les citations
import random
import json
from urllib.request import urlopen
from urllib.parse import quote_plus
from parse_env import parse_env
# parse the api_key from the env
dico_env = parse_env("giphy.env")
api_key = dico_env['api_key']
limit = "25"
def gif_search(query, limit=limit, api_key=api_key):
query = quote_plus(query) # make the query browser compliant
data = json.loads(urlopen("http://api.giphy.com/v1/gifs/search?q="+query+"&api_key="+api_key+"&limit="+limit).read())
random_id = random.randint(0, int(limit) -1 )
#print("GIPHY API: ", random_id)
id = data['data'][random_id]['id']
#print("GIPHY API: ", id)
url_gif = "https://i.giphy.com/media/"+id+"/giphy.gif"
mime = "image/gif" # mime type of the image
data_bytes = urlopen(url_gif).read()
return data_bytes, mime
import traceback
import re
from matrix_client.client import MatrixClient
from matrix_client.api import MatrixRequestError
class MatrixBotAPI:
# username - Matrix username
# password - Matrix password
# server - Matrix server url : port
# rooms - List of rooms ids to operate in, or None to accept all rooms
def __init__(self, username, password, server, rooms=None):
self.username = username
# Authenticate with given credentials
self.client = MatrixClient(server)
try:
self.client.login_with_password(username, password)
except MatrixRequestError as e:
print(e)
if e.code == 403:
print("Bad username/password")
except Exception as e:
print("Invalid server URL")
traceback.print_exc()
# Store allowed rooms
self.rooms = rooms
# Store empty list of handlers
self.handlers = []
# If rooms is None, we should listen for invites and automatically accept them
if rooms is None:
self.client.add_invite_listener(self.handle_invite)
self.rooms = []
# Add all rooms we're currently in to self.rooms and add their callbacks
for room_id, room in self.client.get_rooms().items():
room.add_listener(self.handle_message)
self.rooms.append(room_id)
else:
# Add the message callback for all specified rooms
for room in self.rooms:
room.add_listener(self.handle_message)
def add_handler(self, handler):
self.handlers.append(handler)
def handle_message(self, room, event):
# Make sure we didn't send this message
if re.match("@" + self.username, event['sender']):
return
# Loop through all installed handlers and see if they need to be called
for handler in self.handlers:
if handler.test_callback(room, event):
# This handler needs to be called
try:
handler.handle_callback(room, event)
except:
traceback.print_exc()
def handle_invite(self, room_id, state):
print("Got invite to room: " + str(room_id))
print("Joining...")
room = self.client.join_room(room_id)
# Add message callback for this room
room.add_listener(self.handle_message)
# Add room to list
self.rooms.append(room)
def start_polling(self):
# Starts polling for messages
self.client.start_listener_thread()
return self.client.sync_thread
def upload_to_server(self, data, mime):
mxc = self.client.upload(data, mime)
return mxc
"""
Defines a matrix bot handler for commands
"""
import re
from matrix_bot_api.mhandler import MHandler
class MCommandHandler(MHandler):
# command - String of command to handle
# handle_callback - Function to call if message contains command
# cmd_char - Character that denotes a command. '!' by default
def __init__(self, command, handle_callback, cmd_char='!'):
MHandler.__init__(self, self.test_command, handle_callback)
self.command = command
self.cmd_char = cmd_char
# Function called by Matrix bot api to determine whether or not to handle this message
def test_command(self, room, event):
# Test the message to see if it has our command
if event['type'] == "m.room.message":
if re.match(self.cmd_char + self.command, event['content']['body']):
return True
return False
"""
Defines a Matrix bot message handler
"""
class MHandler(object):
# test_callback - function that takes a room and event and returns a boolean
# indicating whether we should pass the message on to handle_callback
#
# handle_callback - function that takes a room and event and handles them
def __init__(self, test_callback, handle_callback):
self.test_callback = test_callback
self.handle_callback = handle_callback
"""
Defines a matrix bot handler that uses regex to determine if message should be handled
"""
import re
from matrix_bot_api.mhandler import MHandler
class MRegexHandler(MHandler):
# regex_str - Regular expression to test message against
#
# handle_callback - Function to call if messages matches regex
def __init__(self, regex_str, handle_callback):
MHandler.__init__(self, self.test_regex, handle_callback)
self.regex_str = regex_str
def test_regex(self, room, event):
# Test the message and see if it matches the regex
if event['type'] == "m.room.message":
if re.search(self.regex_str, event['content']['body']):
# The message matches the regex, return true
return True
return False
"""
A test bot using the Python Matrix Bot API
Test it out by adding it to a group chat and doing one of the following:
1. Say "Hi"
2. Say !echo this is a test!
3. Say !d6 to get a random size-sided die roll result
"""
import random
from matrix_bot_api.matrix_bot_api import MatrixBotAPI
from matrix_bot_api.mregex_handler import MRegexHandler
from matrix_bot_api.mcommand_handler import MCommandHandler
from giphy_api import gif_search
from parse_env import parse_env
# Parse the global variables from the env
dico_env = parse_env("matrix.env")
USERNAME = dico_env['USERNAME'] # Bot's username
PASSWORD = dico_env['PASSWORD'] # Bot's password
SERVER = dico_env['SERVER'] # Matrix server URL
# define the bot object
bot = MatrixBotAPI(USERNAME, PASSWORD, SERVER)
# define the callbacks
def hi_callback(room, event):
# Somebody said hi, let's say Hi back
room.send_text("Bonjour, " + event['sender'])
def echo_callback(room, event):
args = event['content']['body'].split()
args.pop(0)
# Echo what they said back
room.send_text(' '.join(args))
def dieroll_callback(room, event):
args = event['content']['body'].split()
die = args[0]
die_max = die[2:]
# ensure the die is a positive integer
if not die_max.isdigit():
room.send_text('{} is not a positive number!'.format(die_max))
return
# and ensure it's a reasonable size, to prevent bot abuse
die_max = int(die_max)
if die_max <= 1 or die_max >= 1000:
room.send_text('dice must be between 1 and 1000!')
return
# finally, send the result back
result = random.randrange(1,die_max+1)
room.send_text(str(result))
def help_callback(room, event):
args = event['content']['body'].split()
if len(args) == 1:
room.send_text("""Commandes disponibles :
!help quelquechose permet de trouver de l'aide sur quelquechose
!dn donne le résultat d'un dé à n faces. n compris entre 1 et 1000
!echo renvoie le même message que l'argument passé""")
elif args[1] == "d" or "dice" or "die" or "dés" or "dé":
room.send_text("""Aide sur la commande d :
Utilisation : envoyer un message de contenant uniquement !d accolé à un nombre.
Ce nombre doit être compris en 1 et 1000.""")
def gif_callback(room, event):
args = event['content']['body'].split()
query = " ".join(args[1:])
print("gif called with query={} in room={}".format(query, room.display_name))
data, mime = gif_search(query)
mxc_url = bot.upload_to_server(data, mime)
# cette fonction est une réécriture degeux -> à refaire !
room.send_image(mxc_url, query, mimetype=mime)
def main():
# Create an instance of the MatrixBotAPI
#bot = MatrixBotAPI(USERNAME, PASSWORD, SERVER)
# Add a regex handler waiting for the word Hi
hi_handler = MRegexHandler("Bonjour|Bonsoir|bonjour|bonsoir|Salut|salut", hi_callback)
bot.add_handler(hi_handler)
# Add a regex handler waiting for the echo command
echo_handler = MCommandHandler("echo", echo_callback)
bot.add_handler(echo_handler)
# Add a regex handler waiting for the die roll command
dieroll_handler = MCommandHandler("d", dieroll_callback)
bot.add_handler(dieroll_handler)
help_handler = MCommandHandler("help", help_callback)
bot.add_handler(help_handler)
gif_handler = MCommandHandler("gif", gif_callback)
bot.add_handler(gif_handler)
# Start polling
bot.start_polling()
# Infinitely read stdin to stall main thread while the bot runs in other threads
while True:
input()
if __name__ == "__main__":
main()
def parse_env(file):
f = open(file, "r")
L = f.readlines()
f.close()
dico = {}
for k in range(len(L)):
L[k] = L[k].strip("\n")
L[k] = L[k].split(" ")
dico[L[k][0]] = L[k][2]
return dico
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment