diff --git a/Gemfile b/Gemfile index 841fcda..37e59b4 100644 --- a/Gemfile +++ b/Gemfile @@ -10,4 +10,4 @@ gem 'i18n', '0.7.0' gem 'nokogiri', '1.8.1' gem 'countries', '2.1.4' gem 'cinch', '2.3.4' -gem 'em-eventsource', github: 'MusikAnimal/em-eventsource', branch: 'master' +gem 'em-eventsource', path: '~/dev/em-eventsource' diff --git a/Gemfile.lock b/Gemfile.lock index 33024fa..749dcd2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,12 +1,3 @@ -GIT - remote: git://github.com/MusikAnimal/em-eventsource.git - revision: 3af86d5a7b4a52dd142de9deff204152e81df869 - branch: master - specs: - em-eventsource (0.3.0) - em-http-request (~> 1.0) - eventmachine (~> 1.0) - GIT remote: git://github.com/MusikAnimal/mediawiki-gateway.git revision: e809559f5b796b73f534e51235b5e4c8c06f6ab0 @@ -15,6 +6,13 @@ GIT mediawiki-gateway (1.2.5) rest-client (~> 2.0) +PATH + remote: ../em-eventsource + specs: + em-eventsource (0.3.0) + em-http-request (~> 1.0) + eventmachine (~> 1.0) + GEM remote: https://rubygems.org/ specs: diff --git a/config/application.example.yml b/config/application.example.yml index ed319f8..c500b3b 100644 --- a/config/application.example.yml +++ b/config/application.example.yml @@ -16,6 +16,11 @@ replica: port: 4711 username: username password: password +abusefilter_rc: + host: 127.0.0.1 + port: 4713 + username: username + password: password copypatrol: # private database on enwiki.labsdb host: 127.0.0.1 port: 4711 diff --git a/tasks/abusefilter_irc.rb b/tasks/abusefilter_irc.rb index 43603de..93fcad7 100644 --- a/tasks/abusefilter_irc.rb +++ b/tasks/abusefilter_irc.rb @@ -3,19 +3,39 @@ require 'cinch' require 'em-eventsource' +### +# Schema for the database: +# +# CREATE TABLE subscriptions ( +# id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, +# project VARCHAR(30) NOT NULL, +# user VARCHAR(255) NOT NULL, +# filter_id INT NOT NULL +# ); +# CREATE UNIQUE INDEX proj_user_sub ON subscriptions (project, user, filter_id); +### + module AbuseFilterIRC include Cinch::Plugin - CHANNEL = '#wikipedia-en-abuse-log-all' + CHANNELS = { + # 'enwiki' => '#wikipedia-en-abuse-log-all', + # 'enwiki' => '##MusikBot', + 'commons.wikimedia.org' => '#wikimedia-commons-abuse-log', + 'en.wikipedia.org' => '##MusikBot' + } + UNSUBSCRIBE_MSG = 'To unsubscribe, use !unsubscribe [filter ID] or !unsubscribe all' def self.run $mb = MusikBot::Session.new(inspect, true, true) + $client = $mb.repl_client(credentials: :toolsdb, log: false) + $subscriptions = nil bot = Cinch::Bot.new do configure do |c| c.server = 'chat.freenode.org' - c.channels = [CHANNEL] + c.channels = CHANNELS.values c.nick = $mb.app_config[:irc][:nick] c.password = $mb.app_config[:irc][:password] c.user = $mb.app_config[:irc][:user] @@ -30,6 +50,26 @@ def authed(user) true end + + def load_subscriptions + $subscriptions = {} + + $client.query('SELECT * FROM subscriptions').to_a.each do |row| + $subscriptions[row['project']] ||= {} + $subscriptions[row['project']][row['user']] ||= [] + $subscriptions[row['project']][row['user']] << row['filter_id'] + end + end + + def validate_project(user, project) + if !CHANNELS.keys.include?(project) + user.send "#{project} is an invalid project or is currently unsupported." + user.send "Supported projects include: " + CHANNELS.keys.join(', ') + return false + end + + true + end end on :connect do @@ -38,11 +78,11 @@ def authed(user) source.on "message" do |message| data = JSON.parse(message) - if data['wiki'] == 'enwiki' && data['log_action'] == 'hit' + if CHANNELS.keys.include?(data['server_name']) && data['log_action'] == 'hit' msg = "User:#{data['user'].tr(' ', '_')} tripped *filter-#{data['log_params']['filter']}* on [[#{data['title']}]]: " \ - "https://en.wikipedia.org/wiki/Special:AbuseLog/#{data['log_params']['log']}" + "https://#{data['server_name']}/wiki/Special:AbuseLog/#{data['log_params']['log']}" - Channel(CHANNEL).send(msg) + Channel(CHANNELS[data['server_name']]).send(msg) $mb.local_storage['subscriptions'].each do |user, filter_ids| if filter_ids.include?(data['log_params']['filter'].to_i) @@ -68,24 +108,28 @@ def authed(user) m.reply 'Pong.' end - on :message, /!subscribe (\d+)/ do |m, filter_id| + on :message, /!subscribe (\w+\.\w+\.org) (\d+)/ do |m, project, filter_id| return unless authed(m.user) + return unless validate_project(m.user, project) - storage = $mb.local_storage - storage['subscriptions'][m.user.nick] ||= [] - storage['subscriptions'][m.user.nick] |= [filter_id.to_i] - storage['subscriptions'] - $mb.local_storage(storage) + statement = $client.prepare('INSERT INTO subscriptions VALUES(NULL, ?, ?, ?)') + statement.execute(project, m.user.nick, filter_id.to_i) + + load_subscriptions m.user.send "You have subscribed to filter #{filter_id}" m.user.send UNSUBSCRIBE_MSG end - on :message, /!unsubscribe (\d+|all)/ do |m, filter_id| + on :message, /!unsubscribe (\w+\.\w+\.org) (\d+|all)/ do |m, project, filter_id| return unless authed(m.user) + return unless validate_project(m.user, project) + + statement = $client.prepare('DELETE FROM subscriptions WHERE project = ? AND user = ? AND filter_id = ?') + statement.execute(project, m.user.nick, filter_id.to_i) + + load_subscriptions - storage = $mb.local_storage - storage['subscriptions'][m.user.nick] ||= [] if 'all' == filter_id storage['subscriptions'][m.user.nick] = [] m.user.send "You have unsubscribed to all filters" @@ -96,10 +140,30 @@ def authed(user) $mb.local_storage(storage) end - on :message, /!subscriptions/ do |m| + on :message, /!subscriptions (\w+\.\w+\.org)/ do |m, project| return unless authed(m.user) - subscriptions = ($mb.local_storage['subscriptions'][m.user.nick] rescue []).map(&:to_i).sort.join(', ') + if project.present? + return unless validate_project(m.user, project) + + statement = $client.prepare('SELECT filter_id FROM subscriptions WHERE project = ? AND user = ?') + subscriptions = statement.execute(project, m.user.nick).to_a.collect { |row| row['filter_id'] }.join(', ') + else + statement = $client.prepare('SELECT project, filter_id FROM subscriptions WHERE user = ?') + + data = {} + statement.execute(project, m.user.nick).to_a.each do |row| + data[row['project']] ||= {} + data[row['project']] << row['filter_id'] + end + + subscriptions = '' + data.each do |proj, filter_ids| + subscriptions += proj + ' = ' + filter_ids.join(', ') + '; ' + end + subscriptions.chomp!('; ') + end + m.user.send "You are subscribed to the following filters: #{subscriptions}" m.user.send UNSUBSCRIBE_MSG end