First commit
This commit is contained in:
parent
a75bd9c3c9
commit
246bff21f8
|
@ -102,3 +102,6 @@ venv.bak/
|
|||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
games.db
|
||||
*.swp
|
||||
|
|
13
README.md
13
README.md
|
@ -1,2 +1,15 @@
|
|||
# rpgbot
|
||||
RPG helper bot for Telegram
|
||||
|
||||
Commands:
|
||||
|
||||
- /newgame
|
||||
- /delgame
|
||||
- /showgame
|
||||
- /roll
|
||||
- /player
|
||||
- /update
|
||||
- /add
|
||||
- /del
|
||||
- /show
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# Example config file
|
||||
|
||||
# API token
|
||||
bot_token = 'MY-TG-BOT-TOKEN'
|
||||
|
||||
# Telegram admin id
|
||||
admins = [ADMIN-ID]
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Example config file
|
||||
|
||||
# API token
|
||||
bot_token = '709882140:AAFegBBjsAk4TEiDBvI5xP_hBArun9ok5L8'
|
||||
|
||||
# Telegram admin id
|
||||
admins = [101097946]
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
import sqlite3
|
||||
|
||||
db_name = 'games.db'
|
||||
db_version = 1
|
||||
ROLE_PLAYER = 10
|
||||
ROLE_MASTER = 20
|
||||
|
||||
def open_connection():
|
||||
return sqlite3.connect(db_name)
|
||||
def close_connection(db):
|
||||
db.close()
|
||||
|
||||
def table_exists(db, table):
|
||||
c = db.cursor()
|
||||
query = c.execute('''SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?''', (table,))
|
||||
result = query.fetchone()
|
||||
if result[0] == 0:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def init():
|
||||
db = open_connection()
|
||||
try:
|
||||
print('Initializing database...')
|
||||
c = db.cursor()
|
||||
if not table_exists(db, 'Games'):
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS Games (gameid integer primary key autoincrement, version int, lastactivity datetime, gamename text)''')
|
||||
if not table_exists(db, 'Groups'):
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS Groups (gameid integer, groupid integer primary key, groupname text)''')
|
||||
if not table_exists(db, 'Players'):
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS Players (gameid integer, playerid integer, role integer, playername text, PRIMARY KEY(gameid, playerid))''')
|
||||
if not table_exists(db, 'Contents'):
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS Contents (gameid integer, playerid integer, container text, key text, value text, PRIMARY KEY(gameid, playerid, container, key))''')
|
||||
except:
|
||||
print('failed to initialize database')
|
||||
raise
|
||||
db.commit()
|
||||
close_connection(db)
|
||||
|
||||
def new_game(db, admin, playername, gamename, groupid, groupname):
|
||||
c = db.cursor()
|
||||
query = c.execute('''INSERT INTO Games(version, lastactivity, gamename) VALUES (?, datetime('now'), ?)''', (db_version, gamename,))
|
||||
gameid = c.lastrowid
|
||||
query = c.execute('''INSERT INTO Groups(gameid, groupid, groupname) VALUES (?, ?, ?)''', (gameid, groupid, groupname,))
|
||||
add_player(db, admin, playername, gameid, ROLE_MASTER)
|
||||
db.commit()
|
||||
|
||||
def del_game(db, gameid):
|
||||
c = db.cursor()
|
||||
query = c.execute('''DELETE FROM Games WHERE gameid=?''', (gameid,))
|
||||
query = c.execute('''DELETE FROM Groups WHERE gameid=?''', (gameid,))
|
||||
query = c.execute('''DELETE FROM Players WHERE gameid=?''', (gameid,))
|
||||
query = c.execute('''DELETE FROM Contents WHERE gameid=?''', (gameid,))
|
||||
db.commit()
|
||||
|
||||
def add_player(db, userid, username, gameid, role):
|
||||
c = db.cursor()
|
||||
query = c.execute('''SELECT role FROM Players WHERE playerid=? AND gameid=?''', (userid, gameid,))
|
||||
result = query.fetchone()
|
||||
if result is not None and result[0] >= role:
|
||||
role = result[0]
|
||||
|
||||
query = c.execute('''INSERT OR REPLACE INTO Players(gameid, playerid, role, playername) VALUES (?, ?, ?, ?)''', (gameid, userid, role, username,))
|
||||
db.commit()
|
||||
|
||||
def number_of_games(db, user):
|
||||
c = db.cursor()
|
||||
query = c.execute('''SELECT count(*) FROM Players WHERE playerid=?''', (user,))
|
||||
result = query.fetchone()
|
||||
return result[0]
|
||||
|
||||
def get_game_from_group(db, groupid):
|
||||
c = db.cursor()
|
||||
query = c.execute('''SELECT gameid FROM Groups WHERE groupid=?''', (groupid,))
|
||||
result = query.fetchone()
|
||||
return result[0]
|
||||
|
||||
def get_game_info(db, gameid):
|
||||
c = db.cursor()
|
||||
query = c.execute('''SELECT gamename FROM Games WHERE gameid=?''', (gameid,))
|
||||
result = query.fetchone()
|
||||
gamename = result[0]
|
||||
query = c.execute('''SELECT groupname FROM Groups WHERE gameid=?''', (gameid,))
|
||||
groups = []
|
||||
for group in query:
|
||||
groups.append(group[0])
|
||||
query = c.execute('''SELECT playername FROM Players WHERE gameid=?''', (gameid,))
|
||||
players = []
|
||||
for player in query:
|
||||
players.append(player[0])
|
||||
return gamename, groups, players
|
||||
|
||||
def number_of_items(db, gameid, playerid):
|
||||
c = db.cursor()
|
||||
query = c.execute('''SELECT count(*) FROM Contents WHERE gameid=? AND playerid=?''', (gameid, playerid,))
|
||||
result = query.fetchone()
|
||||
return result[0]
|
||||
|
||||
def update_item(db, gameid, playerid, container, key, change, relative):
|
||||
c = db.cursor()
|
||||
query = c.execute('''SELECT value FROM Contents WHERE gameid=? AND playerid=? AND container=? AND key=?''', (gameid, playerid, container, key,))
|
||||
result = query.fetchone()
|
||||
if result is None:
|
||||
oldvalue = 0
|
||||
else:
|
||||
oldvalue = int(result[0])
|
||||
if relative:
|
||||
newvalue = oldvalue + change
|
||||
else:
|
||||
newvalue = change
|
||||
query = c.execute('''INSERT OR REPLACE INTO Contents(gameid, playerid, container, key, value) VALUES (?, ?, ?, ?, ?)''', (gameid, playerid, container, key, newvalue,))
|
||||
db.commit()
|
||||
return oldvalue, newvalue
|
||||
|
||||
def delete_item(db, gameid, playerid, container, key):
|
||||
c = db.cursor()
|
||||
query = c.execute('''SELECT value FROM Contents WHERE gameid=? AND playerid=? AND container=? AND key=?''', (gameid, playerid, container, key,))
|
||||
result = query.fetchone()
|
||||
if result == None:
|
||||
return None
|
||||
oldvalue = result[0]
|
||||
query = c.execute('''DELETE FROM Contents WHERE gameid=? AND playerid=? AND container=? AND key=?''', (gameid, playerid, container, key,))
|
||||
db.commit()
|
||||
return oldvalue
|
||||
|
||||
def get_items(db, gameid, playerid):
|
||||
ret = {}
|
||||
c = db.cursor()
|
||||
query = c.execute('''SELECT DISTINCT container FROM Contents WHERE gameid=? AND playerid=?''', (gameid, playerid,))
|
||||
for container in query:
|
||||
inner = c.execute('''SELECT key, value FROM Contents WHERE gameid=? AND playerid=? AND container=?''', (gameid, playerid, container[0]))
|
||||
my_list = {}
|
||||
for item in inner:
|
||||
my_list[item[0]] = item[1]
|
||||
ret[container[0]] = my_list
|
||||
return ret
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/python
|
||||
import telepot
|
||||
from pprint import pprint
|
||||
import tempconfig
|
||||
|
||||
bot = telepot.Bot(tempconfig.bot_token)
|
||||
|
||||
response = bot.getUpdates()
|
||||
|
||||
# Print all raw messages with chat_id,text,type,username
|
||||
pprint(response)
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/bash
|
||||
printf "Installing Packages...\n"
|
||||
apt-get install -y python nmap dnsutils mtr python-pip && pip install telepot
|
||||
|
||||
printf "\n\n--------------------------------\n\n"
|
||||
echo "Enter your Telegram BOT Token. "
|
||||
read -r TG_BOT_TOKEN
|
||||
|
||||
echo "bot_token = '$TG_BOT_TOKEN'" > tempconfig.py
|
||||
|
||||
printf "\n\n--------------------------------\n\n"
|
||||
echo "Fetching last Telegram messages. This will help finding your Telegram ID."
|
||||
echo "If you can't see your Telegram ID, try sending a private message to this bot."
|
||||
python get-sender-id.py | grep "'id'" | uniq -c | awk '{ print $3 }' | sed s'/,//'
|
||||
rm tempconfig.py
|
||||
|
||||
echo "Enter your Telegram ID. This will be the default admin user."
|
||||
read -r SENDER_ID
|
||||
|
||||
cp config.example.py config.py
|
||||
sed -i s"/MY-TG-BOT-TOKEN/$TG_BOT_TOKEN/" config.py
|
||||
sed -i s"/MY-SENDER-ID-LIST/$SENDER_ID/" config.py
|
||||
|
||||
printf "\n\n--------------------------------\n\n"
|
||||
echo "Do you want to configure the daemon with systemctl? (y/n)"
|
||||
read -r DAEMON
|
||||
case $DAEMON in
|
||||
N|n)
|
||||
exit 0
|
||||
;;
|
||||
Y|y)
|
||||
cp systemctl/rpgbot.example.service /tmp/rpgbot.service
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
sed -i s"#MY-PATH#$DIR#" /tmp/rpgbot.service
|
||||
mv /tmp/rpgbot.service /etc/systemd/system/multi-user.target.wants/rpgbot.service
|
||||
systemctl daemon-reload
|
||||
systemctl restart tsh
|
||||
;;
|
||||
*)
|
||||
echo Unrecognized option $DAEMON, exiting
|
||||
exit 1
|
||||
;;
|
||||
esac
|
|
@ -0,0 +1,204 @@
|
|||
#!/usr/bin/env python
|
||||
# Telegram RPG Character Sheet bot
|
||||
|
||||
from pprint import pprint
|
||||
import telepot
|
||||
import time
|
||||
import socket
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import random
|
||||
|
||||
import config
|
||||
import db
|
||||
|
||||
log_file = 'service.log'
|
||||
|
||||
|
||||
class Operation:
|
||||
def __init__(self, id, type, name):
|
||||
self.id = id
|
||||
self.type = type
|
||||
self.name = name
|
||||
def __eq__(self, other):
|
||||
return self.id == other.id
|
||||
|
||||
def die():
|
||||
os.kill(os.getpid(), signal.SIGINT)
|
||||
|
||||
def fatal(msg):
|
||||
print(msg)
|
||||
die()
|
||||
|
||||
def log(msg):
|
||||
f = open(log_file, 'a')
|
||||
f.write(msg + "\n")
|
||||
f.close()
|
||||
print(msg)
|
||||
|
||||
def log_msg(msg):
|
||||
chat_name = ''
|
||||
username = msg['from']['username']
|
||||
text = msg['text']
|
||||
if 'title' in msg['chat']:
|
||||
chat_name = '{} ({})'.format(msg['chat']['title'], username)
|
||||
else:
|
||||
chat_name = username
|
||||
log('{}: {}'.format(chat_name, text))
|
||||
|
||||
def send(bot, chat_id, msg):
|
||||
if msg == None or len(msg) == 0 or len(msg.split()) == 0:
|
||||
msg = '(no message)'
|
||||
bot.sendMessage(chat_id, msg)
|
||||
|
||||
def process_message(msg):
|
||||
"""
|
||||
Process received messages.
|
||||
|
||||
msg -- The received message
|
||||
"""
|
||||
chat_id = msg['chat']['id']
|
||||
sender_id = msg['from']['id']
|
||||
username = msg['from']['username']
|
||||
text = msg['text']
|
||||
is_group = msg['chat']['type'] == 'group'
|
||||
groupname = msg['chat']['title'] if 'title' in msg['chat'] else None
|
||||
|
||||
# avoid logging and processing every single message.
|
||||
if text[0] != '/':
|
||||
return
|
||||
|
||||
log_msg(msg)
|
||||
dbc = db.open_connection()
|
||||
is_admin = False
|
||||
if sender_id in config.admins:
|
||||
is_admin = True
|
||||
|
||||
args=text.split(maxsplit=1)
|
||||
command = args[0]
|
||||
if command == '/newgame':
|
||||
if not is_group:
|
||||
send(bot, chat_id, 'You must run this command in a group.')
|
||||
return
|
||||
if len(args) < 2:
|
||||
send(bot, chat_id, 'Please specify the game name.')
|
||||
return
|
||||
if db.number_of_games(dbc, sender_id) > 10:
|
||||
send(bot, chat_id, 'You exceeded the maximum number of games. Please close some first.')
|
||||
return
|
||||
db.new_game(dbc, sender_id, username, args[1], chat_id, groupname)
|
||||
send(bot, chat_id, 'New game created: {}.'.format(args[1]))
|
||||
if command == '/delgame':
|
||||
if not is_group:
|
||||
send(bot, chat_id, 'You must run this command in a group.')
|
||||
return
|
||||
# TODO
|
||||
if not is_admin:
|
||||
send(bot, chat_id, 'You need to be the administrator to close a game.')
|
||||
return
|
||||
gameid = db.get_game_from_group(dbc, chat_id)
|
||||
if gameid is None:
|
||||
send(bot, chat_id, 'No game found.')
|
||||
return
|
||||
db.del_game(dbc, gameid)
|
||||
send(bot, chat_id, 'GG, humans.')
|
||||
if command == '/showgame':
|
||||
if not is_group:
|
||||
send(bot, chat_id, 'You must run this command in a group.')
|
||||
return
|
||||
gameid = db.get_game_from_group(dbc, chat_id)
|
||||
gamename, groups, players = db.get_game_info(dbc, gameid)
|
||||
ret = '{}\nGroups: {}\nPlayers: {}'.format(gamename, ', '.join(groups), ', '.join(players))
|
||||
send(bot, chat_id, ret)
|
||||
|
||||
if command == '/roll':
|
||||
ret = 0
|
||||
string = ''
|
||||
for i in range(0, 4):
|
||||
value = random.randint(-1, 1)
|
||||
ret += value;
|
||||
if value == -1:
|
||||
string += '➖'
|
||||
if value == 0:
|
||||
string += '〇'
|
||||
if value == 1:
|
||||
string += '➕'
|
||||
send(bot, chat_id, 'Rolled {} = {}.'.format(string, ret))
|
||||
|
||||
if command == '/player':
|
||||
if not is_group:
|
||||
send(bot, chat_id, 'You must run this command in a group.')
|
||||
return
|
||||
if len(args) < 2:
|
||||
send(bot, chat_id, 'Please specify the player name.')
|
||||
return
|
||||
if db.number_of_games(dbc, sender_id) > 10:
|
||||
send(bot, chat_id, 'You exceeded the maximum number of games. Please close some first.')
|
||||
return
|
||||
gameid = db.get_game_from_group(dbc, chat_id)
|
||||
db.add_player(dbc, sender_id, args[1], gameid, db.ROLE_PLAYER)
|
||||
send(bot, chat_id, 'Welcome, {}.'.format(args[1]))
|
||||
|
||||
if command == '/update' or command == '/add':
|
||||
if not is_group:
|
||||
send(bot, chat_id, 'You must run this command in a group.')
|
||||
return
|
||||
gameid = db.get_game_from_group(dbc, chat_id)
|
||||
if db.number_of_items(dbc, gameid, sender_id) > 50:
|
||||
send(bot, chat_id, 'You exceeded the maximum number of items. Please delete some first.')
|
||||
return
|
||||
args = args[1].split()
|
||||
if len(args) != 3:
|
||||
send(bot, chat_id, 'Use the format: [container] [key] [change].')
|
||||
return
|
||||
(container, key, change) = args
|
||||
if command == '/update':
|
||||
relative = False
|
||||
else:
|
||||
relative = True
|
||||
oldvalue, newvalue = db.update_item(dbc, gameid, sender_id, container, key, int(change), relative=relative)
|
||||
send(bot, chat_id, 'Updated {}/{} from {} to {} (changed {}).'.format(container, key,
|
||||
oldvalue, newvalue, newvalue-oldvalue))
|
||||
if command == '/del':
|
||||
if not is_group:
|
||||
send(bot, chat_id, 'You must run this command in a group.')
|
||||
return
|
||||
gameid = db.get_game_from_group(dbc, chat_id)
|
||||
args = args[1].split()
|
||||
if len(args) != 2:
|
||||
send(bot, chat_id, 'Use the format: [container] [key].')
|
||||
return
|
||||
(container, key) = args
|
||||
oldvalue = db.delete_item(dbc, gameid, sender_id, container, key)
|
||||
if oldvalue == None:
|
||||
send(bot, chat_id, 'No item by that name.')
|
||||
else:
|
||||
send(bot, chat_id, 'Deleted {}/{} (was {}).'.format(container, key, oldvalue))
|
||||
|
||||
if command == '/show':
|
||||
if not is_group:
|
||||
send(bot, chat_id, 'You must run this command in a group.')
|
||||
return
|
||||
gameid = db.get_game_from_group(dbc, chat_id)
|
||||
items = db.get_items(dbc, gameid, sender_id)
|
||||
ret = ''
|
||||
if items is None:
|
||||
send(bot, chat_id, 'No items found.')
|
||||
return
|
||||
for container in items:
|
||||
ret += container + ':\n'
|
||||
for key, value in items[container].items():
|
||||
ret += ' - {} ({})\n'.format(key,value)
|
||||
send(bot, chat_id, ret)
|
||||
|
||||
db.close_connection(dbc)
|
||||
|
||||
db.init()
|
||||
bot = telepot.Bot(config.bot_token)
|
||||
print('Entering message loop.')
|
||||
bot.message_loop(process_message)
|
||||
|
||||
while 1:
|
||||
time.sleep(10)
|
|
@ -0,0 +1,13 @@
|
|||
[Unit]
|
||||
Description=RPG Bot daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=MY-PATH
|
||||
ExecStart=MY-PATH/main.py
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
Loading…
Reference in New Issue