ninjabot.py 7.77 KB
Newer Older
Benjamin Graillot's avatar
Benjamin Graillot committed
1 2
import json
import os
Benjamin Graillot's avatar
Benjamin Graillot committed
3
import re
Benjamin Graillot's avatar
Benjamin Graillot committed
4
import select
Benjamin Graillot's avatar
Benjamin Graillot committed
5
import threading
Benjamin Graillot's avatar
Benjamin Graillot committed
6
import time
Benjamin Graillot's avatar
Benjamin Graillot committed
7 8 9

from bot import Bot
import config
Benjamin Graillot's avatar
Benjamin Graillot committed
10
from subs import subs
Benjamin Graillot's avatar
Benjamin Graillot committed
11

Benjamin Graillot's avatar
Benjamin Graillot committed
12

Benjamin Graillot's avatar
Benjamin Graillot committed
13
def write_subs(subs):
Benjamin Graillot's avatar
Benjamin Graillot committed
14
	with open('subs.py', 'w') as s:
Benjamin Graillot's avatar
Benjamin Graillot committed
15
		s.write('subs={' + ','.join([repr(source)+':{' + ','.join([repr(nick) + ':{' + ','.join([repr(channel) + ':[' + ','.join([repr(patt) for patt in subs[source][nick][channel]]) + ']' for channel in subs[source][nick]]) + '}' for nick in subs[source]]) + '}' for source in subs]) + ' }\n')
Benjamin Graillot's avatar
Benjamin Graillot committed
16

Benjamin Graillot's avatar
Benjamin Graillot committed
17 18 19 20 21 22 23 24 25
def subscribed(channel):
    global subs
    for source in subs:
         for nick in subs[source]:
             if channel in subs[source][nick]:
                 return True
    return False


26
class Ninja(Bot):
Benjamin Graillot's avatar
Benjamin Graillot committed
27
	def __init__(self):
Benjamin Graillot's avatar
Benjamin Graillot committed
28
		Bot.__init__(self, "NinjaBot")
Benjamin Graillot's avatar
Benjamin Graillot committed
29 30 31
		self.sources = []

	def on_welcome_ext(self, conn, e):
Benjamin Graillot's avatar
Benjamin Graillot committed
32
		conn.join('#wikistalk')
Benjamin Graillot's avatar
Benjamin Graillot committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
		channels = set()
		for source in subs:
			for nick in subs[source]:
				for channel in subs[source][nick]:
					if channel[0] in config.channel_start:
						channels.add(channel)
		for channel in channels:
			conn.join(channel)
		self.thread = threading.Thread(target=self.loop)
		self.thread.start()

	def loop(self):
		try:
			os.remove(config.socket_path)
		except OSError:
			pass
		os.mkfifo(config.socket_path)
		while True:
51
			fifo = open(config.socket_path)
Benjamin Graillot's avatar
Benjamin Graillot committed
52
			data = fifo.read()
53
			fifo.close()
Benjamin Graillot's avatar
Benjamin Graillot committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
			try:
				if data.startswith('SOURCES '):
					self.sources = data[8:].split(',')
					print('Sources are now :', data[8:])
				else:
					data = json.loads(data)
					source = data['source']
					page = data['page']
					message = data['message']
					self.push_update(source, page, message)
			except:
				continue

	def push_update(self, source, page, message):
		global subs
		patts = {}
		for nick in subs[source]:
			for channel in subs[source][nick]:
				if channel not in patts: patts[channel] = []
				patts[channel] += subs[source][nick][channel]
		for channel in patts:
			if any(re.fullmatch(patt, page) for patt in patts[channel]):
				t = time.time()
				self.connection.privmsg(channel, message)
				# on attend 5 secondes pour éviter que le serveur nous empêche d'envoyer les messages
				# time.sleep n'a pas l'air de trop marcher dans les thread du coup je le combine avec time.time
				while t + 5 > time.time():
					time.sleep(1)

	def do_command_ext(self, conn, command, level, nick):
		global subs
		for source in self.sources:
			if source not in subs:
				subs[source] = {}
			if nick not in subs[source]:
				subs[source][nick] = {}
		if command[0].casefold() == "help":
			conn.privmsg(nick, "Sources : " + ", ".join(self.sources))
			conn.privmsg(nick, "Commandes (arguments optionnels entre []) :")
			conn.privmsg(nick, " - sub source (#channel|yournick) regex : abonner #channel ou soi-même aux modifications sur les pages matchant regex (au format python).")
			conn.privmsg(nick, " - list [source] : lister ses abonnements.")
			conn.privmsg(nick, " - del [source] [#channel|yournick] [n] : supprimer le nième abonnement donné par list.")
			if level >= 100:
				conn.privmsg(nick, " - listall : lister tous les abonnements.")
			if level >= float('inf'):
				conn.privmsg(nick, " - sudo nick command : exécuter la commande en tant que nick (attention : les éventuels messages privés de réponse seront envoyés à cette personne).")
		elif command[0].casefold() == "sub":
			if len(command) >= 4:
				patt = ' '.join(command[3:])
				correct_source = False
				for source in self.sources:
					if source == command[1]:
						correct_source = True
						break
				if not correct_source:
					conn.privmsg(nick, "Source {source} incorrecte".format(source=command[1]))
					return
				channel = command[2] if command[2][0] in config.channel_start else nick
				if channel not in subs[source][nick]:
					subs[source][nick][channel] = []
					if channel[0] in config.channel_start:
						conn.join(channel)
				subs[source][nick][channel].append(patt)
				conn.privmsg(nick, "Vous avez abonné {channel} à {patt} sur {source}".format(channel=channel, patt=patt, source=command[1]))
		elif command[0].casefold() == "del":
			if len(command) == 1:
				channels = set()
				for source in subs:
					for channel in subs[source][nick]:
						if channel[0] in config.channel_start:
							channels.add(channel)
					subs[source][nick] = {}
				conn.privmsg(nick, "Tous les abonnements ont été supprimés")
				for channel in channels:
					if not subscribed(channel):
						conn.part(channel, "Plus d'abonnement sur {channel}".format(channel=channel))
				return
			correct_source = False
			for source in self.sources:
				if source == command[1]:
					correct_source = True
					break
			if not correct_source:
				conn.privmsg(nick, "Source {source} incorrecte".format(source=command[1]))
				return
			elif len(command) == 2:
				channels = { channel for channel in subs[source][nick] if channel[0] in config.channel_start }
				subs[source][nick] = {}
				conn.privmsg(nick, "Tous les abonnments provenant de {source} ont été supprimés".format(source=command[1]))
				for channel in channels:
					if not subscribed(channel):
						conn.part(channel, "Plus d'abonnement sur {channel}".format(channel=channel))
				return
Benjamin Graillot's avatar
Benjamin Graillot committed
147
			channel = command[2] if command[2][0] in config.channel_start else nick
Benjamin Graillot's avatar
Benjamin Graillot committed
148 149 150 151
			if channel not in subs[source][nick]:
				conn.privmsg(nick, "Vous n'avez pas d'abonnement à {source} sur {channel}".format(source=command[1], channel=channel))
				return
			elif len(command) == 3:
Benjamin Graillot's avatar
Benjamin Graillot committed
152 153
				if channel in subs[source][nick]:
					del subs[source][nick][channel]
Benjamin Graillot's avatar
Benjamin Graillot committed
154 155 156 157 158
				conn.privmsg(nick, "Vos abonnements à {source} sur {channel} on été supprimés".format(source=command[1], channel=channel))
				if channel[0] in config.channel_start:
					if not subscribed(channel):
						conn.part(channel, "Plus d'abonnement sur {channel}".format(channel=channel))
			elif len(command) == 4:
Benjamin Graillot's avatar
Benjamin Graillot committed
159 160
				if command[3].isnumeric():
					n = int(command[3])
Benjamin Graillot's avatar
Benjamin Graillot committed
161 162 163
					if n < len(subs[source][nick][channel]):
						subscription = subs[source][nick][channel][n]
						del subs[source][nick][channel][n]
Benjamin Graillot's avatar
Benjamin Graillot committed
164
						conn.privmsg(nick, "Votre abonnement, {subscription}, à {source} sur {channel} a été supprimé".format(subscription=subscription, source=command[1], channel=channel))
Benjamin Graillot's avatar
Benjamin Graillot committed
165 166
						if len(subs[source][nick][channel]) == 0:
							del subs[source][nick][channel]
Benjamin Graillot's avatar
Benjamin Graillot committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
							if channel[0] in config.channel_start:
								if not subscribed(channel):
									conn.part(channel, "Plus d'abonnement sur {channel}".format(channel=channel))
		elif command[0].casefold() == "list":
			if len(command) == 1:
				for source in subs:
					conn.privmsg(nick, source + ' :')
					for channel in subs[source][nick]:
						conn.privmsg(nick, ' - ' + channel + ' :')
						for i, patt in enumerate(subs[source][nick][channel]):
							 conn.privmsg(nick, '   + {i}. {patt}'.format(i=i, patt=patt))
				return
			correct_source = False
			for source in self.sources:
				if source == command[1]:
					correct_source = True
					break
			if not correct_source:
				conn.privmsg(nick, "Source {source} incorrecte".format(source=command[1]))
				return
			if len(command) == 2:
				for channel in subs[source][nick]:
					conn.privmsg(nick, channel + ' :')
					for i, patt in enumerate(subs[source][nick][channel]):
						 conn.privmsg(nick, ' - {i}. {patt}'.format(i=i, patt=patt))
		elif command[0].casefold() == "sudo":
			if len(command) >= 3 and level == float('inf'):
				self.do_command_ext(conn, command[2:], 0, command[1])
		elif command[0].casefold() == "listall" and level >= 100:
			for source in subs:
Benjamin Graillot's avatar
Benjamin Graillot committed
197
				conn.privmsg(nick, source + ':')
Benjamin Graillot's avatar
Benjamin Graillot committed
198 199 200 201 202 203 204 205 206 207 208 209
				for n in subs[source]:
					conn.privmsg(nick, ' - ' + n + ':')
					for channel in subs[source][n]:
						conn.privmsg(nick, '   + ' + channel)
						for i, patt in enumerate(subs[source][n][channel]):
							conn.privmsg(nick, '     * {i}. {patt}'.format(i=i, patt=patt))
		write_subs(subs)


if __name__ == "__main__":
	ninja = Ninja()
	ninja.start()