Compare commits
	
		
			24 Commits
		
	
	
		
			3525ebdccc
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 149ab033b0 | |||
| f3b74b65ed | |||
| 17eb4e8bdd | |||
| 7bdebb7fd3 | |||
| bb9a9f7a9b | |||
| 0434bf5854 | |||
| 0238610cd1 | |||
| c4afd9139e | |||
| a9ed7aa159 | |||
| 489dccaa4e | |||
| 47592d5c44 | |||
| 2d377081b6 | |||
| a5ed267b32 | |||
| e075c24306 | |||
| 7639027684 | |||
| 1632bbfbe2 | |||
| a982ee0525 | |||
| 31bd289f32 | |||
| 4dee25a43b | |||
| f88e29426d | |||
| d7c4f7e14c | |||
| 68a4d440e6 | |||
| 27a2d3e50e | |||
| 7581a15266 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -114,3 +114,4 @@ dmypy.json | |||||||
| # Pyre type checker | # Pyre type checker | ||||||
| .pyre/ | .pyre/ | ||||||
|  |  | ||||||
|  | config.py | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,2 +1,23 @@ | |||||||
| # minetest-telegram-bridge | # minetest-telegram-bridge | ||||||
|  |  | ||||||
|  | # Usage | ||||||
|  |  | ||||||
|  | + Install `python3`, `flask`, `python-telegram-bot` | ||||||
|  | + `cd` | ||||||
|  | + `git clone https://git.webionite.com/ceda_ei/minetest-telegram-bridge` | ||||||
|  | + `ln -s ~/minetest-telegram-bridge/chat_mod ~/.minetest/mods/chat_mod` | ||||||
|  | + Add mod to `secure.http_mods`. | ||||||
|  | 	+ Add this line if `secure.http_mods` doesn't exist. `secure.http_mods = chat_mod` | ||||||
|  | + Enable the mod in `world.mt` | ||||||
|  | + `cd minetest-telegram-bridge/bot` | ||||||
|  | + Copy `sample.config.py` to `config.py` and edit. | ||||||
|  | + `python3 bot.py &` | ||||||
|  | + `export FLASK_APP=server.py` | ||||||
|  | + `export FLASK_ENV=production` | ||||||
|  | + `flask run -p 9876` | ||||||
|  | + Start the minetest server via `minetest [params] |& python3 pipe_in.py` | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Configuration | ||||||
|  |  | ||||||
|  | To use a different port, change the respective config in `config.py` and add `chat_server_port = PORT` in `minecraft.conf` and run `flask run -p PORT` instead of the last step. | ||||||
|   | |||||||
							
								
								
									
										106
									
								
								bot/bot.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								bot/bot.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
|  | import config | ||||||
|  | import logging | ||||||
|  | import requests | ||||||
|  | import re | ||||||
|  | from emoji import demojize | ||||||
|  | from telegram.ext import Updater, CommandHandler, MessageHandler, Filters | ||||||
|  |  | ||||||
|  | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - \ | ||||||
|  |                     %(message)s', level=logging.INFO) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_text_or_type(msg, bot, truncate=False): | ||||||
|  |     message = "" | ||||||
|  |  | ||||||
|  |     # Set message contents | ||||||
|  |     if msg.text: | ||||||
|  |         if msg.from_user.username == bot.username and re.match('<.*?>(.*)', | ||||||
|  |                                                                msg.text): | ||||||
|  |             x = re.compile('(<.*?>)(.*)') | ||||||
|  |             y = x.search(msg.text) | ||||||
|  |             z = y.group(2) | ||||||
|  |             message += (z if len(z) <= 20 or not truncate else z[0:19] + "...") | ||||||
|  |         else: | ||||||
|  |             message += (msg.text if len(msg.text) <= 20 or not truncate else | ||||||
|  |                         msg.text[0:19] + "...") | ||||||
|  |     elif msg.audio: | ||||||
|  |         message += "[Audio]" | ||||||
|  |     elif msg.document: | ||||||
|  |         message += "[Document]" | ||||||
|  |     elif msg.animation: | ||||||
|  |         message += "[GIF]" | ||||||
|  |     elif msg.game: | ||||||
|  |         message += "[Game]" | ||||||
|  |     elif msg.photo: | ||||||
|  |         message += "[Photo]" | ||||||
|  |     elif msg.sticker: | ||||||
|  |         message += "[Sticker]" | ||||||
|  |     elif msg.video: | ||||||
|  |         message += "[Video]" | ||||||
|  |     elif msg.voice: | ||||||
|  |         message += "[Voice Note]" | ||||||
|  |     elif msg.video_note: | ||||||
|  |         message += "[Video Note]" | ||||||
|  |     elif msg.contact: | ||||||
|  |         message += "[Contact]" | ||||||
|  |     elif msg.location: | ||||||
|  |         message += "[Location]" | ||||||
|  |     elif msg.venue: | ||||||
|  |         message += "[Venue]" | ||||||
|  |     else: | ||||||
|  |         message += "[Unknown type]" | ||||||
|  |  | ||||||
|  |     # If caption exists | ||||||
|  |     if msg.caption: | ||||||
|  |         message += (msg.caption if len(msg.caption) <= 20 or not truncate else | ||||||
|  |                     msg.caption[0:19] + "...") | ||||||
|  |  | ||||||
|  |     # Set sender | ||||||
|  |     return_message = "" | ||||||
|  |     for i in message.strip().split("\n"): | ||||||
|  |         if msg.from_user.username != bot.username or not re.match('<.*?>(.*)', | ||||||
|  |                                                                   msg.text): | ||||||
|  |             if msg.from_user.username: | ||||||
|  |                 return_message += f"@{msg.from_user.username}: " | ||||||
|  |             else: | ||||||
|  |                 return_message += f"{msg.from_user.first_name}: " | ||||||
|  |         else: | ||||||
|  |             x = re.compile('(<.*?>)(.*)') | ||||||
|  |             y = x.search(msg.text) | ||||||
|  |             return_message = y.group(1) + ": " + i + "\n" | ||||||
|  |         # Set the replied to message's contents | ||||||
|  |         if msg.reply_to_message: | ||||||
|  |             return_message += ("[> " + | ||||||
|  |                                get_text_or_type(msg.reply_to_message, bot, | ||||||
|  |                                                 True).split("\n")[0] + | ||||||
|  |                                f"] ") | ||||||
|  |         return_message += i + "\n" | ||||||
|  |  | ||||||
|  |     return return_message.strip() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def message_handling(bot, update): | ||||||
|  |     if update.message.chat.id in config.groups: | ||||||
|  |         message = get_text_or_type(update.message, bot) | ||||||
|  |         params = {"message": demojize(message)} | ||||||
|  |         requests.post("http://localhost:" + str(config.port) + "/post", | ||||||
|  |                       data=params) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def direct_cmd(bot, update, text): | ||||||
|  |     if update.message.chat.id in config.groups: | ||||||
|  |         params = {"message": text} | ||||||
|  |         requests.post("http://localhost:" + str(config.port) + "/post", | ||||||
|  |                       data=params) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | updater = Updater(token=config.api_key) | ||||||
|  | dispatcher = updater.dispatcher | ||||||
|  | dispatcher.add_handler(CommandHandler('list', lambda x, y: direct_cmd(x, y, | ||||||
|  |                                                                       "list"))) | ||||||
|  | dispatcher.add_handler(CommandHandler('time', lambda x, y: direct_cmd(x, y, | ||||||
|  |                                                                       "time"))) | ||||||
|  | dispatcher.add_handler(MessageHandler(Filters.all, message_handling)) | ||||||
|  | updater.start_polling() | ||||||
							
								
								
									
										7
									
								
								bot/pipe_in.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								bot/pipe_in.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | import sys | ||||||
|  | import re | ||||||
|  |  | ||||||
|  | for line in sys.stdin: | ||||||
|  |     if not re.match(".* Mod performs HTTP request with URL ", line): | ||||||
|  |         print(line, end="") | ||||||
							
								
								
									
										9
									
								
								bot/sample.config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								bot/sample.config.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | # Create a new bot by messaging @BotFather and follow the instructions | ||||||
|  | # Replace the key by the actual token recieved from BotFather | ||||||
|  | api_key = "123456789:xxxxxxxxxxxxxxxxxxxxxxxxxxxx" | ||||||
|  |  | ||||||
|  | # Port to connect to server | ||||||
|  | port = 9876 | ||||||
|  |  | ||||||
|  | # Groups to read from | ||||||
|  | groups = [] | ||||||
							
								
								
									
										1
									
								
								bot/schema.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								bot/schema.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | CREATE TABLE messages(message text); | ||||||
							
								
								
									
										64
									
								
								bot/server.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								bot/server.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
|  | from flask import Flask, request | ||||||
|  | import sqlite3 | ||||||
|  | import config | ||||||
|  | import telegram | ||||||
|  | import re | ||||||
|  | from emoji import emojize | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def escape_html(message): | ||||||
|  |     return re.sub("<", "<", | ||||||
|  |                   re.sub(">", "&rt;", | ||||||
|  |                          re.sub("&", "&", message))) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get(db): | ||||||
|  |     cursor = db.cursor() | ||||||
|  |     query = "SELECT * FROM messages ORDER BY ROWID" | ||||||
|  |     cursor.execute(query) | ||||||
|  |     text = "" | ||||||
|  |     for row in cursor: | ||||||
|  |         text += row[0] + "\n" | ||||||
|  |     cursor.execute("DELETE FROM messages") | ||||||
|  |     db.commit() | ||||||
|  |     return text | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def post(db): | ||||||
|  |     message = request.form['message'] | ||||||
|  |     cursor = db.cursor() | ||||||
|  |     query = 'INSERT INTO messages values(?)' | ||||||
|  |     cursor.execute(query, (message,)) | ||||||
|  |     db.commit() | ||||||
|  |     return "" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def send(bot, code): | ||||||
|  |     message = request.form["message"] | ||||||
|  |     if code: | ||||||
|  |         message = escape_html(message) | ||||||
|  |         message = "<code>" + message + "</code>" | ||||||
|  |         for i in config.groups: | ||||||
|  |             bot.send_message(chat_id=i, text=emojize(message), | ||||||
|  |                              parse_mode="HTML") | ||||||
|  |     else: | ||||||
|  |         for i in config.groups: | ||||||
|  |             bot.send_message(chat_id=i, text=emojize(message)) | ||||||
|  |     return "" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | app = Flask(__name__) | ||||||
|  | db = sqlite3.connect(":memory:", check_same_thread=False) | ||||||
|  | bot = telegram.Bot(token=config.api_key) | ||||||
|  |  | ||||||
|  | # Set up tables | ||||||
|  | cursor = db.cursor() | ||||||
|  | with open('schema.sql') as fp: | ||||||
|  |     cursor.executescript(fp.read()) | ||||||
|  |  | ||||||
|  | app.add_url_rule('/get', 'get', lambda: get(db)) | ||||||
|  | app.add_url_rule('/post', 'post', lambda: post(db), methods=["POST"]) | ||||||
|  | app.add_url_rule('/send', 'send', lambda: send(bot, False), methods=["POST"]) | ||||||
|  | app.add_url_rule('/sendCode', 'sc', lambda: send(bot, True), methods=["POST"]) | ||||||
							
								
								
									
										72
									
								
								chat_mod/init.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								chat_mod/init.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | local http_api = minetest.request_http_api() | ||||||
|  |  | ||||||
|  | function tablelength(T) | ||||||
|  |   local count = 0 | ||||||
|  |   for _ in pairs(T) do count = count + 1 end | ||||||
|  |   return count | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function send_messages(res) | ||||||
|  | 	for message in string.gmatch(res["data"], "[^\n]+") do | ||||||
|  | 		if message == "list" then | ||||||
|  | 			local x = minetest.get_connected_players() | ||||||
|  | 			local text = "List of Players (" | ||||||
|  | 			text = text .. tostring(tablelength(x)) .. "/" | ||||||
|  | 			text = text .. tostring(minetest.settings:get("max_users")) .. "):" | ||||||
|  | 			for j, i  in ipairs(x) do | ||||||
|  | 				text = text .. "\n" .. i.get_player_name(i) | ||||||
|  | 			end | ||||||
|  | 			http_api.fetch_async({url = "http://localhost:" .. port .. "/sendCode", | ||||||
|  | 					post_data = {message = text}}) | ||||||
|  | 		elseif message == "time" then | ||||||
|  | 			local x = minetest.get_timeofday() | ||||||
|  | 			local text = string.format("%02d:%02d", math.floor(x*24), math.floor((x*24*60) % 60)) | ||||||
|  | 			http_api.fetch_async({url = "http://localhost:" .. port .. "/sendCode", | ||||||
|  | 					post_data = {message = text}}) | ||||||
|  | 		else | ||||||
|  | 			minetest.chat_send_all(message) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function main() | ||||||
|  | 	port = minetest.settings:get("chat_server_port") | ||||||
|  | 	if port == nil then | ||||||
|  | 		port = "9876" | ||||||
|  | 	end | ||||||
|  | 	http_api.fetch({url = "http://localhost:" .. port .. "/get"}, send_messages) | ||||||
|  | 	minetest.after(0.5, main) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function dead(player) | ||||||
|  | 	http_api.fetch_async({url = "http://localhost:" .. port .. "/sendCode", | ||||||
|  | 					post_data = {message = player.get_player_name(player) .. | ||||||
|  | 											" died."}}) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function join(player) | ||||||
|  | 	http_api.fetch_async({url = "http://localhost:" .. port .. "/sendCode", | ||||||
|  | 					post_data = {message = player.get_player_name(player) .. | ||||||
|  | 											" joined the server."}}) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function left(player) | ||||||
|  | 	http_api.fetch_async({url = "http://localhost:" .. port .. "/sendCode", | ||||||
|  | 					post_data = {message = player.get_player_name(player) .. | ||||||
|  | 											" left the server."}}) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function chat(name, message) | ||||||
|  | 	http_api.fetch_async({url = "http://localhost:" .. port .. "/send", | ||||||
|  | 					post_data = {message = "<" .. name .. "> " .. message}}) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | if http_api == nil then | ||||||
|  | 	minetest.log("error", "Chat Mod is not in secure.http_mods or secure.trusted_mods. Exiting.") | ||||||
|  | else | ||||||
|  | 	main() | ||||||
|  | end | ||||||
|  | minetest.register_on_dieplayer(dead) | ||||||
|  | minetest.register_on_joinplayer(join) | ||||||
|  | minetest.register_on_leaveplayer(left) | ||||||
|  | minetest.register_on_chat_message(chat) | ||||||
		Reference in New Issue
	
	Block a user