#!/usr/bin/env python3 import http import hmac import json import socket import threading import logging from flask import Flask, request from pprint import pprint from hashlib import sha256 from phabricator import Phabricator from time import sleep website = "https://phab.lubuntu.me" phab = Phabricator(host=website+"/api/", token="API KEY") global username username = "lugito" server = "irc.freenode.net" port = 6667 channel = "#lubuntu-devel" logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.DEBUG) logger = logging.getLogger(__name__) app = Flask(__name__) def connecttoirc(): global conn, username conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn.connect((server, port)) setup = False usersuffix = 0 while setup == False: response = conn.recv(512).decode("utf-8") if "No Ident response" in response: conn.send("NICK {}\r\n".format(username).encode("utf-8")) conn.send("USER {} * * :{}\r\n".format(username, username).encode("utf-8")) if "376" in response: conn.send("JOIN {}\r\n".format(channel).encode("utf-8")) if "433" in response: usersuffix = usersuffix + 1 username = username + str(usersuffix) conn.send("NICK {}\r\n".format(username).encode("utf-8")) conn.send("USER {} * * :{}\r\n".format(username, username).encode("utf-8")) if "PING" in response: conn.send("PONG :{}\r\n".format(response.split(":")[1]).encode("utf-8")) if "366" in response: setup = True logger.info("Successfully connected to the IRC server.") def isnewtask(task): modified = None for data in task: if modified: if data["dateCreated"] == data["dateModified"] and data["dateCreated"] == modified: modified = data["dateCreated"] newtask = True else: newtask = False break else: modified = data["dateCreated"] return newtask def sendnotice(message): conn.send("NOTICE {} :{}\r\n".format(channel, message).encode("utf-8")) def ircmessage(objectstr, who, body, link): # e.g. [T31: Better IRC integration] message = "\x033[\x03\x0313" + objectstr + "\x03\x033]\x03 " # e.g. tsimonq2 (Simon Quigley) message = message + "\x0315" + who + "\x03 " # e.g. commented on the task: message = message + body + ": " # e.g. https://phab.lubuntu.me/T40#779 message = message + "\x032" + link + "\x03" # Make sure we can debug this if it goes haywire logger.debug(message) # Sleep for a second, so when we have a bunch of messages we have a buffer sleep(1) # Aaaaand, send it off! sendnotice(message) def gettaskinfo(task): sendmessage = "" try: # We only need the task number. taskinfo = phab.maniphest.info(task_id=int(task.split("T")[1])) sendmessage = sendmessage + "\x033[\x03" # The color of the priority text should correspond to its value. color = taskinfo["priorityColor"] if color == "violet": sendmessage = sendmessage + "\x036Needs Triage" elif color == "pink": sendmessage = sendmessage + "\x035Unbreak Now!" elif color == "red": sendmessage = sendmessage + "\x034High" elif color == "orange": sendmessage = sendmessage + "\x037Medium" elif color == "yellow": sendmessage = sendmessage + "\x038Low" elif color == "sky": sendmessage = sendmessage + "\x037Wishlist" # Put the task status in the message. sendmessage = sendmessage + ", " + taskinfo["statusName"] + "\x03" sendmessage = sendmessage + "\x033]\x03 " # Put the title in there as well. sendmessage = sendmessage + taskinfo["title"].strip() + ": " # And the link. sendmessage = sendmessage + "\x032" + taskinfo["uri"] + "\x03" # Send it off! sendnotice(sendmessage) # If someone wrote something like "Tblah", obviously that's not right. except ValueError: sendnotice("\x034Error: " + task.strip() + "is an invalid task reference.\x03") return None def ircbot(message, msgtype): if msgtype == "info": message = message.split(" :" + username + ": info")[1] for item in message.split(): if item.startswith("T"): gettaskinfo(item.strip()) elif msgtype == "link": for item in message.split("https://phab.lubuntu.me/"): if item.split()[0].strip().startswith("T"): gettaskinfo(item.split()[0].strip()) else: sendnotice("\x034Error: unknown command.\x03") return None def listenirc(): while True: ircmsg = conn.recv(512) if len(ircmsg) == 0: logger.warn("Connection lost, reconnecting!") connecttoirc() continue ircmsg = ircmsg.decode("UTF-8").strip('\n\r') logger.debug(ircmsg) if ircmsg.find("PING :") != -1: conn.send(bytes("PONG :pingis\n", "UTF-8")) elif ircmsg.find(" :" + username + ": info") != -1: ircbot(ircmsg, "info") elif ircmsg.find("https://phab.lubuntu.me/T") != -1: ircbot(ircmsg, "link") @app.route("/irc", methods=["POST"]) def main(): data = request.data hash = hmac.new(bytes(u"HMAC KEY", "utf-8"), data, sha256) # We MUST ensure that the request came from Phab. if hash.hexdigest() == request.headers["X-Phabricator-Webhook-Signature"]: data = json.loads(data) logger.debug(data) exists = True try: # Try to find the object. search = phab.transaction.search(objectIdentifier=data["object"]["phid"])["data"] # Find the author too. userlookup = search[0]["authorPHID"] who = str(dict(phab.phid.query(phids=[userlookup]))[userlookup]["fullName"]) # If the object exists, no worries, let's just return a good response. except http.client.HTTPException: exists = False if exists: logger.debug("Object exists, checking to see if it's a task or a commit.") if data["object"]["type"] == "TASK": logger.debug("This is a task. Checking if it's new.") newtask = isnewtask(search) if newtask: logger.debug("Yes, it's a new task.") else: logger.debug("No, it's not a new task.") # If it's not a new task, let's see if it's a comment, and if it's just an edit. comment = None commentid = None edited = None if not newtask: commentid = None edited = False for task in search: dataepoch = data["action"]["epoch"] datemodified = task["dateModified"] # All comments within ten seconds of the request are fair game. if datemodified >= (dataepoch - 10) and datemodified <= (dataepoch + 10) and task["comments"] != []: logger.debug("It's a comment, yes.") comment = True commentid = task["id"] if datemodified != task["dateCreated"]: logger.debug("The comment was edited.") edited = True else: logger.debug("The comment was NOT edited.") edited = False break else: comment = False if comment or edited or newtask: objectstr = phab.phid.query(phids=[data["object"]["phid"]])[data["object"]["phid"]]["fullName"] if comment: body = "commented on the task" elif edited: body = "edited a message on the task" elif newtask: body = "just created this task" # Assuming this is a comment, there should always be a URI associated with it. link = phab.phid.query(phids=[data["object"]["phid"]])[data["object"]["phid"]]["uri"] # Even though this can be off sometimes, let's include the comment ID in the link too. # FIXME: Make this more accurate, and figure out why it's inaccurate at times. if commentid: link = link + "#" + str(commentid) ircmessage(objectstr, who, body, link) elif data["object"]["type"] == "CMIT": logger.debug("It's a commit!") commitphid = data["object"]["phid"] objectstr = phab.phid.query(phids=[commitphid])[commitphid]["fullName"] body = "committed" # The URI for this one is waaaaaaay too long. Let's assemble it ourselves. link = website + "/" + phab.phid.query(phids=[commitphid])[commitphid]["name"] ircmessage(objectstr, who, body, link) return "OK" if __name__ == "__main__": connecttoirc() t = threading.Thread(target=listenirc) t.daemon = True t.start() app.run(host="0.0.0.0", port=5000)