You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
7.4 KiB

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# S.D.G
"""
:mod:`lugito.connectors.irc`
======================================
Define an irc connector class
.. currentmodule:: lugito.connectors.irc
"""
# Imports
import ssl
import http
import socket
import logging
import threading
import phabricator
from time import sleep
class IRCConnector(object):
def __init__(self, log_level=logging.DEBUG):
# IRC info
# Read the configuration out of the .arcconfig file
self.host = phabricator.ARCRC['irc']['host']
self.port = int(phabricator.ARCRC['irc']['port'])
self.username = phabricator.ARCRC['irc']['username']
self.password = phabricator.ARCRC['irc']['password']
self.channel = phabricator.ARCRC['irc']['channel']
# Phabricator info
self.phab = phabricator.Phabricator()
self.phab_host = phabricator.ARCRC['config']['default'].replace(
'api/', '')
self.logger = logging.getLogger('lugito.connector.IRCConnector')
# Add log level
ch = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
self.logger.addHandler(ch)
self.logger.setLevel(log_level)
def _send_raw(self, message):
"""Low level send"""
self.conn.send(message.encode('utf-8'))
def _socket_conn(self):
self.conn = ssl.wrap_socket(
socket.socket(socket.AF_INET, socket.SOCK_STREAM))
self.conn.connect((self.host, self.port))
def connect(self):
"""Connect"""
self._socket_conn()
setup = False
usersuffix = 0
self.logger.info("Connecting to IRC.")
while not setup:
response = self.conn.recv(512).decode("utf-8")
self.logger.debug(response)
if "No Ident response" in response:
self._send_raw("NICK {}\r\n".format(self.username))
self._send_raw("USER {} * * :{}\r\n".format(
self.username, self.username))
self._send_raw("PRIVMSG nickserv :identify {} {}\r\n".format(
self.username, self.password))
if "You are now identified" in response:
sleep(5)
self._send_raw("JOIN {}\r\n".format(self.channel))
if "477" in response:
sleep(5)
self._send_raw("JOIN {}\r\n".format(self.channel))
if "433" in response:
usersuffix = usersuffix + 1
self.username = self.username + str(usersuffix)
self._send_raw("NICK {}\r\n".format(self.username))
self._send_raw("USER {} * * :{}\r\n".format(
self.username, self.username))
if "PING" in response:
self._send_raw("PONG :{}\r\n".format(response.split(":")[1]))
if "366" in response:
setup = True
self.logger.info("Successfully connected to the IRC server.")
def send_notice(self, message):
self._send_raw("NOTICE {} :{}\r\n".format(self.channel, message))
def send(self, objectstr, who, body, link):
"""Send a formatted message"""
# 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
self.logger.debug(message)
# Sleep for a fifth of a second, so when we have a bunch of messages we have a buffer
sleep(0.2)
# Aaaaand, send it off!
self.send_notice(message)
def gettaskinfo(self, task):
sendmessage = ""
# Strip out anchor link
# This will prevent invalid task / diff references
anchor = None
if '#' in task:
task, anchor = task.split('#')
try:
# We only need the task number.
if len(task.split("T")) > 1:
taskinfo = self.phab.maniphest.info(
task_id=int(task.split("T")[1]))
# or the diff number
elif len(task.split("D")) > 1:
taskinfo = self.phab.differential.query(
ids=[int(task.split("D")[1])])[0]
taskinfo['priorityColor'] = None
sendmessage += "\x033[\x03"
# The color of the priority text should correspond to its value.
color = taskinfo["priorityColor"]
if color == "violet":
sendmessage += "\x036Needs Triage"
elif color == "pink":
sendmessage += "\x035Unbreak Now!"
elif color == "red":
sendmessage += "\x034High"
elif color == "orange":
sendmessage += "\x037Medium"
elif color == "yellow":
sendmessage += "\x038Low"
elif color == "sky":
sendmessage += "\x037Wishlist"
# Put the task status in the message.
if color is not None:
sendmessage += ", "
sendmessage += taskinfo["statusName"] + "\x03\x033]\x03 "
# Put the title in there as well.
sendmessage += taskinfo["title"].strip() + ": "
# And the link.
sendmessage += "\x032" + taskinfo["uri"]
# Add the anchor back if it was present
if anchor is not None:
sendmessage += '#{}'.format(anchor)
sendmessage += '\x03'
# Send it off!
self.send_notice(sendmessage)
# If someone wrote something like "Tblah", obviously that's not right.
except ValueError:
self.send_notice("\x034Error: " + task.strip() + "is an invalid task reference.\x03")
return None
def bot(self, message, msgtype):
if msgtype == "info":
message = message.split(" :" + self.username + ": info")[1]
for item in message.split():
if item.startswith("T") or item.startwith("D"):
self.gettaskinfo(item.strip())
elif msgtype == "link":
for item in message.split(self.phab_host):
if (item.split()[0].strip().startswith("T")) or \
(item.split()[0].strip().startswith("D")):
self.gettaskinfo(item.split()[0].strip())
else:
self.sendnotice("\x034Error: unknown command.\x03")
return None
def listen(self):
while True:
ircmsg = self.conn.recv(512)
if len(ircmsg) == 0:
# logger.warn is deprecated, use .warning.
self.logger.warning("Connection lost, reconnecting!")
self.connect()
continue
ircmsg = ircmsg.decode("UTF-8").strip('\n\r')
self.logger.debug(ircmsg)
if ircmsg.find("PING :") != -1:
self.conn.send(bytes("PONG :pingis\n", "UTF-8"))
elif ircmsg.find(" :" + self.username + ": info") != -1:
self.bot(ircmsg, "info")
elif ircmsg.find("{}".format(self.phab_host)) != -1:
self.bot(ircmsg, "link")