add redmine bot
This commit is contained in:
parent
ab2aec6f95
commit
ab9013a947
16
bots/redminebot/LICENSE
Normal file
16
bots/redminebot/LICENSE
Normal file
@ -0,0 +1,16 @@
|
||||
Copyright (C) 2017 Benjamin Drieu
|
||||
|
||||
This is heavily derived from IRC bot for Redmine, whose licence is:
|
||||
|
||||
IRC bot for Redmine
|
||||
Copyright (C) 2011 Jasmin Rahimic
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
194
bots/redminebot/redminebot.py
Executable file
194
bots/redminebot/redminebot.py
Executable file
@ -0,0 +1,194 @@
|
||||
#!/usr/bin/python -u
|
||||
# -*- coding: utf-8 -1 -*-
|
||||
|
||||
# Import some necessary libraries.
|
||||
import socket, sys, time, csv, Queue, random, re, pdb, select, os.path, datetime
|
||||
from threading import Thread
|
||||
import feedparser
|
||||
import xml.dom.minidom
|
||||
from time import mktime, localtime
|
||||
|
||||
import iso8601
|
||||
|
||||
|
||||
# IRC configuration
|
||||
|
||||
default_server = "irc.eu.freenode.net"
|
||||
default_nickname = "agirbot"
|
||||
help_list = ["help", "faq", "aide"]
|
||||
hello_list = ["hello", "yo", "bonjour", "salut"]
|
||||
|
||||
#########################
|
||||
### Class Definitions ###
|
||||
#########################
|
||||
|
||||
class Project(object):
|
||||
def __init__(self, project, channel):
|
||||
self.name = project
|
||||
self.channel = channel
|
||||
self.redmine_next = datetime.datetime.utcnow()
|
||||
self.redmine_latest = datetime.datetime.utcnow()
|
||||
|
||||
def set_ircsock ( self, ircsock ):
|
||||
self.ircsock = ircsock
|
||||
|
||||
def redmine(self):
|
||||
t = datetime.datetime.utcnow()
|
||||
# print "Running: %s (%s) for %s" % ( t, self.redmine_next, self.channel )
|
||||
if t >= self.redmine_next:
|
||||
latest_new = self.redmine_latest
|
||||
|
||||
self.redmine_next = self.redmine_next + datetime.timedelta(seconds=10)
|
||||
redmine = feedparser.parse("http://agir.april.org/projects/%s/activity.atom?show_issues=1" % self.name)
|
||||
|
||||
for i in reversed(range(len(redmine.entries))):
|
||||
e = redmine.entries[i]
|
||||
et = e.updated_parsed
|
||||
td = iso8601.parse_date(e.updated)
|
||||
if td.replace(tzinfo=None) > self.redmine_latest:
|
||||
msg = "Redmine: (%s): %s" % (e.link, e.title)
|
||||
self.ircsock.send("PRIVMSG {0} :{1}\n".format(self.channel, msg.encode('utf-8', 'ignore')))
|
||||
|
||||
# find the new latest time
|
||||
if td.replace(tzinfo=None) > latest_new:
|
||||
latest_new = td.replace(tzinfo=None)
|
||||
self.redmine_latest = latest_new
|
||||
|
||||
|
||||
# Defines a bot
|
||||
class Bot(object):
|
||||
|
||||
def __init__(self, server, botnick):
|
||||
self.botnick = botnick
|
||||
self.hello_regex = re.compile(self.get_regex(hello_list), re.I)
|
||||
self.help_regex = re.compile(self.get_regex(help_list), re.I)
|
||||
self.server = server
|
||||
self.projects = [ ]
|
||||
|
||||
def connect(self):
|
||||
self.ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.ircsock.connect((self.server, 6667))
|
||||
self.ircsock.send("USER {0} {0} {0} :Robot Agir April"
|
||||
".\n".format(self.botnick)) # bot authentication
|
||||
self.ircsock.send("NICK {}\n".format(self.botnick)) # Assign the nick to the bot.
|
||||
if os.path.isfile("password.txt"):
|
||||
with open("password.txt", 'r') as f:
|
||||
password = f.read()
|
||||
if registered == True:
|
||||
self.ircsock.send("PRIVMSG {} {} {} {}".format("NickServ","IDENTIFY", self.botnick, password))
|
||||
|
||||
def add_project(self, project):
|
||||
project.set_ircsock ( self.ircsock )
|
||||
self.ircsock.send("JOIN {} \n".format(project.channel)) # Joins channel
|
||||
self.projects.append(project)
|
||||
|
||||
def get_project(self, name):
|
||||
for project in self.projects:
|
||||
if name[0] != '#' and project.name == name:
|
||||
return project
|
||||
elif name[0] == '#' and project.channel == name:
|
||||
return project
|
||||
|
||||
# Main loop
|
||||
def loop(self):
|
||||
last_read = datetime.datetime.utcnow()
|
||||
while 1: # Loop forever
|
||||
ready_to_read, b, c = select.select([self.ircsock],[],[], 1)
|
||||
if ready_to_read:
|
||||
last_read = datetime.datetime.utcnow()
|
||||
ircmsg = self.msg_handler()
|
||||
ircmsg, actor, channel = self.parse_messages(ircmsg)
|
||||
if ircmsg is not None:
|
||||
self.message_response(ircmsg, actor, channel)
|
||||
if datetime.datetime.utcnow() - last_read > datetime.timedelta(minutes=10):
|
||||
raise Exception('timeout: nothing to read on socket since 10 minutes')
|
||||
|
||||
# Responds to server Pings.
|
||||
def pong(self, ircmsg):
|
||||
response = "PONG :" + ircmsg.split("PING :")[1] + "\n"
|
||||
self.ircsock.send(response)
|
||||
self._redmine()
|
||||
|
||||
def _redmine(self):
|
||||
for project in self.projects:
|
||||
project.redmine()
|
||||
|
||||
# Parses messages and responds to them appropriately.
|
||||
def message_response(self, ircmsg, actor, channel):
|
||||
# If someone talks to (or refers to) the bot.
|
||||
if ( ':!' in ircmsg or self.botnick.lower() in ircmsg.lower() ) and \
|
||||
actor != self.botnick and \
|
||||
"PRIVMSG".lower() in ircmsg.lower():
|
||||
if self.hello_regex.search(ircmsg):
|
||||
self.bot_hello(channel, 'Yo!')
|
||||
if self.help_regex.search(ircmsg):
|
||||
self.bot_help(channel)
|
||||
if 'refresh' in ircmsg.lower():
|
||||
self.ircsock.send("PRIVMSG {} :Raffraîchissement en cours\n".format(channel))
|
||||
project = self.get_project(channel)
|
||||
if project:
|
||||
project.redmine()
|
||||
self.ircsock.send("PRIVMSG {} :Fait !\n".format(channel))
|
||||
|
||||
# If the server pings us then we've got to respond!
|
||||
if ircmsg.find("PING :") != -1:
|
||||
self.pong(ircmsg)
|
||||
|
||||
# Responds to a user that inputs "Hello Mybot".
|
||||
def bot_hello(self, channel, greeting):
|
||||
self.ircsock.send("PRIVMSG {0} :{1}\n".format(channel, greeting))
|
||||
|
||||
# Explains what the bot is when queried.
|
||||
def bot_help(self, channel):
|
||||
self.ircsock.send("PRIVMSG {} :Bonjour, je suis un bot qui reconnaît les options !help, !refresh et !bonjour\n".format(channel))
|
||||
self.ircsock.send("PRIVMSG {} :Périodiquement, je vais afficher les actualités de http://agir.april.org/projects/{}.\n".format(channel,self.get_project(channel).name))
|
||||
|
||||
# Reads the messages from the server and adds them to the Queue and prints
|
||||
# them to the console. This function will be run in a thread, see below.
|
||||
def msg_handler(self): # pragma: no cover (this excludes this function from testing)
|
||||
new_msg = self.ircsock.recv(2048) # receive data from the server
|
||||
new_msg = new_msg.strip('\n\r') # removing any unnecessary linebreaks
|
||||
|
||||
if new_msg != '' and new_msg.find("PING :") == -1:
|
||||
print(datetime.datetime.now().isoformat() + " " + new_msg)
|
||||
return new_msg
|
||||
|
||||
# Checks for messages.
|
||||
def parse_messages(self, ircmsg):
|
||||
try:
|
||||
actor = ircmsg.split(":")[1].split("!")[0]
|
||||
try:
|
||||
target = ircmsg.split(":")[1].split(" ")[2]
|
||||
except:
|
||||
target = None
|
||||
return " ".join(ircmsg.split()), actor, target
|
||||
except:
|
||||
# print "Wrong message:", ircmsg
|
||||
return None, None, None
|
||||
|
||||
# Compile regex
|
||||
def get_regex(self, options):
|
||||
pattern = "("
|
||||
for s in options:
|
||||
pattern += s
|
||||
pattern += '|'
|
||||
pattern = pattern[:-1]
|
||||
pattern += ")"
|
||||
return pattern
|
||||
|
||||
|
||||
##########################
|
||||
### The main function. ###
|
||||
##########################
|
||||
|
||||
def main():
|
||||
print datetime.datetime.now().isoformat() + " redmine bot starting…"
|
||||
redmine_bot = Bot(default_server, default_nickname)
|
||||
redmine_bot.connect()
|
||||
redmine_bot.add_project(Project('gdtc','#gdtc'))
|
||||
redmine_bot.add_project(Project('admins','#april-admin'))
|
||||
return redmine_bot.loop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
190
bots/redminebot/redminebot.py~
Executable file
190
bots/redminebot/redminebot.py~
Executable file
@ -0,0 +1,190 @@
|
||||
#!/usr/bin/python -u
|
||||
# -*- coding: utf-8 -1 -*-
|
||||
|
||||
# Import some necessary libraries.
|
||||
import socket, sys, time, csv, Queue, random, re, pdb, select, os.path, datetime
|
||||
from threading import Thread
|
||||
import feedparser
|
||||
import xml.dom.minidom
|
||||
from time import mktime, localtime
|
||||
|
||||
import iso8601
|
||||
|
||||
|
||||
# IRC configuration
|
||||
|
||||
default_server = "irc.eu.freenode.net"
|
||||
default_nickname = "agirbot"
|
||||
help_list = ["help", "info", "faq", "aide"]
|
||||
hello_list = ["hello", "yo", "bonjour", "salut"]
|
||||
|
||||
#########################
|
||||
### Class Definitions ###
|
||||
#########################
|
||||
|
||||
class Project(object):
|
||||
def __init__(self, project, channel):
|
||||
self.name = project
|
||||
self.channel = channel
|
||||
self.redmine_next = datetime.datetime.utcnow()
|
||||
self.redmine_latest = datetime.datetime.utcnow()
|
||||
|
||||
def set_ircsock ( self, ircsock ):
|
||||
self.ircsock = ircsock
|
||||
|
||||
def redmine(self):
|
||||
t = datetime.datetime.utcnow()
|
||||
# print "Running: %s (%s) for %s" % ( t, self.redmine_next, self.channel )
|
||||
if t >= self.redmine_next:
|
||||
latest_new = self.redmine_latest
|
||||
|
||||
self.redmine_next = self.redmine_next + datetime.timedelta(seconds=10)
|
||||
redmine = feedparser.parse("http://agir.april.org/projects/%s/activity.atom?show_issues=1" % self.name)
|
||||
|
||||
for i in reversed(range(len(redmine.entries))):
|
||||
e = redmine.entries[i]
|
||||
et = e.updated_parsed
|
||||
td = iso8601.parse_date(e.updated)
|
||||
if td.replace(tzinfo=None) > self.redmine_latest:
|
||||
msg = "Redmine: (%s): %s" % (e.link, e.title)
|
||||
self.ircsock.send("PRIVMSG {0} :{1}\n".format(self.channel, msg.encode('utf-8', 'ignore')))
|
||||
|
||||
# find the new latest time
|
||||
if td.replace(tzinfo=None) > latest_new:
|
||||
latest_new = td.replace(tzinfo=None)
|
||||
self.redmine_latest = latest_new
|
||||
|
||||
|
||||
# Defines a bot
|
||||
class Bot(object):
|
||||
|
||||
def __init__(self, server, botnick):
|
||||
self.botnick = botnick
|
||||
self.hello_regex = re.compile(self.get_regex(hello_list), re.I)
|
||||
self.help_regex = re.compile(self.get_regex(help_list), re.I)
|
||||
self.server = server
|
||||
self.projects = [ ]
|
||||
|
||||
def connect(self):
|
||||
self.ircsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.ircsock.connect((self.server, 6667))
|
||||
self.ircsock.send("USER {0} {0} {0} :Robot Agir April"
|
||||
".\n".format(self.botnick)) # bot authentication
|
||||
self.ircsock.send("NICK {}\n".format(self.botnick)) # Assign the nick to the bot.
|
||||
if os.path.isfile("password.txt"):
|
||||
with open("password.txt", 'r') as f:
|
||||
password = f.read()
|
||||
if registered == True:
|
||||
self.ircsock.send("PRIVMSG {} {} {} {}".format("NickServ","IDENTIFY", self.botnick, password))
|
||||
|
||||
def add_project(self, project):
|
||||
project.set_ircsock ( self.ircsock )
|
||||
self.ircsock.send("JOIN {} \n".format(project.channel)) # Joins channel
|
||||
self.projects.append(project)
|
||||
|
||||
def get_project(self, name):
|
||||
for project in self.projects:
|
||||
if name[0] != '#' and project.name == name:
|
||||
return project
|
||||
elif name[0] == '#' and project.channel == name:
|
||||
return project
|
||||
|
||||
# Main loop
|
||||
def loop(self):
|
||||
while 1: # Loop forever
|
||||
ready_to_read, b, c = select.select([self.ircsock],[],[], 1)
|
||||
if ready_to_read:
|
||||
ircmsg = self.msg_handler()
|
||||
ircmsg, actor, channel = self.parse_messages(ircmsg)
|
||||
if ircmsg is not None:
|
||||
self.message_response(ircmsg, actor, channel)
|
||||
|
||||
# Responds to server Pings.
|
||||
def pong(self, ircmsg):
|
||||
response = "PONG :" + ircmsg.split("PING :")[1] + "\n"
|
||||
self.ircsock.send(response)
|
||||
self._redmine()
|
||||
|
||||
def _redmine(self):
|
||||
for project in self.projects:
|
||||
project.redmine()
|
||||
|
||||
# Parses messages and responds to them appropriately.
|
||||
def message_response(self, ircmsg, actor, channel):
|
||||
# If someone talks to (or refers to) the bot.
|
||||
if ( ':!' in ircmsg or self.botnick.lower() in ircmsg.lower() ) and \
|
||||
actor != self.botnick and \
|
||||
"PRIVMSG".lower() in ircmsg.lower():
|
||||
if self.hello_regex.search(ircmsg):
|
||||
self.bot_hello(channel, 'Yo!')
|
||||
if self.help_regex.search(ircmsg):
|
||||
self.bot_help(channel)
|
||||
if 'refresh' in ircmsg.lower():
|
||||
self.ircsock.send("PRIVMSG {} :Raffraîchissement en cours\n".format(channel))
|
||||
project = self.get_project(channel)
|
||||
if project:
|
||||
project.redmine()
|
||||
self.ircsock.send("PRIVMSG {} :Fait !\n".format(channel))
|
||||
|
||||
# If the server pings us then we've got to respond!
|
||||
if ircmsg.find("PING :") != -1:
|
||||
self.pong(ircmsg)
|
||||
|
||||
# Responds to a user that inputs "Hello Mybot".
|
||||
def bot_hello(self, channel, greeting):
|
||||
self.ircsock.send("PRIVMSG {0} :{1}\n".format(channel, greeting))
|
||||
|
||||
# Explains what the bot is when queried.
|
||||
def bot_help(self, channel):
|
||||
self.ircsock.send("PRIVMSG {} :Bonjour, je suis un bot qui reconnaît les options !help, !refresh et !bonjour\n".format(channel))
|
||||
self.ircsock.send("PRIVMSG {} :Périodiquement, je vais afficher les actualités de http://agir.april.org/projects/{}.\n".format(channel,self.get_project(channel).name))
|
||||
|
||||
# Reads the messages from the server and adds them to the Queue and prints
|
||||
# them to the console. This function will be run in a thread, see below.
|
||||
def msg_handler(self): # pragma: no cover (this excludes this function from testing)
|
||||
new_msg = self.ircsock.recv(2048) # receive data from the server
|
||||
new_msg = new_msg.strip('\n\r') # removing any unnecessary linebreaks
|
||||
|
||||
if new_msg != '' and new_msg.find("PING :") == -1:
|
||||
print(datetime.datetime.now().isoformat() + " " + new_msg)
|
||||
return new_msg
|
||||
|
||||
# Checks for messages.
|
||||
def parse_messages(self, ircmsg):
|
||||
try:
|
||||
actor = ircmsg.split(":")[1].split("!")[0]
|
||||
try:
|
||||
target = ircmsg.split(":")[1].split(" ")[2]
|
||||
except:
|
||||
target = None
|
||||
return " ".join(ircmsg.split()), actor, target
|
||||
except:
|
||||
# print "Wrong message:", ircmsg
|
||||
return None, None, None
|
||||
|
||||
# Compile regex
|
||||
def get_regex(self, options):
|
||||
pattern = "("
|
||||
for s in options:
|
||||
pattern += s
|
||||
pattern += '|'
|
||||
pattern = pattern[:-1]
|
||||
pattern += ")"
|
||||
return pattern
|
||||
|
||||
|
||||
##########################
|
||||
### The main function. ###
|
||||
##########################
|
||||
|
||||
def main():
|
||||
print datetime.datetime.now().isoformat() + " redmine bot starting…"
|
||||
redmine_bot = Bot(default_server, default_nickname)
|
||||
redmine_bot.connect()
|
||||
redmine_bot.add_project(Project('gdtc','#gdtc'))
|
||||
redmine_bot.add_project(Project('admins','#april-admin'))
|
||||
return redmine_bot.loop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
8
bots/redminebot/redminebot.sh
Executable file
8
bots/redminebot/redminebot.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
BINDIR="`dirname "$0"`"
|
||||
HOMEDIR="$BINDIR"
|
||||
|
||||
cd "$BINDIR"
|
||||
|
||||
nohup $BINDIR/redminebot.py >>"$HOMEDIR/redminebot.log" 2>&1 &
|
11
bots/redminebot/redminebot.sh~
Executable file
11
bots/redminebot/redminebot.sh~
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
BINDIR="`dirname "$0"`"
|
||||
HOMEDIR="$BINDIR"
|
||||
|
||||
echo "BINDIR=$BINDIR"
|
||||
echo "HOMEDIR=$HOMEDIR"
|
||||
|
||||
cd "$BINDIR"
|
||||
|
||||
nohup $BINDIR/redminebot.py >>"$HOMEDIR/redminebot.log" 2>&1 &
|
Loading…
Reference in New Issue
Block a user