Skip to content
Snippets Groups Projects
Commit d38fe92d authored by Yann Leboulanger's avatar Yann Leboulanger
Browse files

Merge branch 'gtk3' into 'gtk3'

[anti_spam] v1.4.3 more in CHANGELOG

Remade for GTK3

See merge request !3
parents f59deb20 d3e2fd54
No related branches found
No related tags found
2 merge requests!41Improved error messages,!3[anti_spam] v1.4.3 more in CHANGELOG
## [Unreleased]
### Added
- Sending confirmation message that anti spam check was passed. It can be disabled in plugin gui config.
## [1.4.3] - 2016-12-04
### Added
- Filtering 'normal' type messages
### Changed
- User from private conference conversation permanently stored in file
- Switched to GTK3
### Fixed
- Messages that was sent before correct answer was marked as received
- Chat between the two antispam plugins
## [0.4.2] - 2016-11-28
### Added
- Anti spam question functionality
- This CHANGELOG
- README with some explanation of functionality
### Changed
- homepage in manifest.ini
# Anti_spam Plugin for Gajim
This Plugin allows you to dissociate itself from the spam.
## Installation
Use special plugin, that manages automatic download and installation of others plugins, it is called Plugin Installer.
## Options
### Block pubsub
Block incoming messages from pubsub
### Message size limit
Block incoming messages that have size more than configured. Default value -1 mean that any sized messages are coming.
### Anti spam question
Block incoming messages from users not in your roster. In response, the Plugin sends a question that you configured. After correct answer(also configurable) you will receive all new messages from user.
**Attention!** All messages before correct answer will be lost.
Also you can enable this function, in Plugin config, for conference private messages. In some servers, the question in conference private does not reach your interlocutor. This can lead to the fact that you will not receive any messages from him, and he will not know it.
\ No newline at end of file
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
## This file is part of Gajim. ## This file is part of Gajim.
## ##
## Gajim is free software; you can redistribute it and/or modify ## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published ## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only. ## by the Free Software Foundation; version 3 only.
## ##
## Gajim is distributed in the hope that it will be useful, ## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of ## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## GNU General Public License for more details.
## ##
## You should have received a copy of the GNU General Public License ## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
''' '''
Block some incoming messages Block some incoming messages
:author: Yann Leboulanger <asterix@lagaule.org> :author: Yann Leboulanger <asterix@lagaule.org>
:since: 16 August 2012 :since: 16 August 2012
:copyright: Copyright (2012) Yann Leboulanger <asterix@lagaule.org> :copyright: Copyright (2012) Yann Leboulanger <asterix@lagaule.org>
:license: GPLv3 :license: GPLv3
''' '''
from gi.repository import Gtk from gi.repository import Gtk
from common import ged import nbxmpp
from common import gajim, ged
from plugins import GajimPlugin
from plugins.helpers import log, log_calls from plugins import GajimPlugin
from plugins.gui import GajimPluginConfigDialog from plugins.helpers import log, log_calls
from plugins.gui import GajimPluginConfigDialog
class AntiSpamPlugin(GajimPlugin):
class AntiSpamPlugin(GajimPlugin):
@log_calls('AntiSpamPlugin')
def init(self): @log_calls('AntiSpamPlugin')
self.description = _('Allows to block some kind of incoming messages') def init(self):
self.config_dialog = AntiSpamPluginConfigDialog(self) self.description = _('Allows to block some kind of incoming messages')
self.config_dialog = AntiSpamPluginConfigDialog(self)
self.gui_extension_points = {
} self.gui_extension_points = {
}
self.events_handlers = {
'atom-entry-received': (ged.POSTCORE, self.events_handlers = {
self._nec_atom_entry_received), 'atom-entry-received': (ged.POSTCORE,
'message-received': (ged.PRECORE, self._nec_atom_entry_received),
self._nec_decrypted_message_received_received), 'message-received': (ged.PRECORE,
'subscribe-presence-received': (ged.POSTCORE, self._nec_message_received_received),
self._nec_subscribe_presence_received), 'decrypted-message-received': (ged.PRECORE,
} self._nec_decrypted_message_received_received),
'subscribe-presence-received': (ged.POSTCORE,
self.config_default_values = { self._nec_subscribe_presence_received),
'block_pubsub_messages': (False, 'If True, Gajim will block incoming messages from pubsub.'), 'message-outgoing': (ged.OUT_PRECORE,
'disable_xhtml_muc': (False, ''), self._nec_message_outgoing)
'disable_xhtml_pm': (False, ''), }
'block_subscription_requests': (False, ''),
} self.config_default_values = {
'block_pubsub_messages': (False, 'If True, Gajim will block incoming messages from pubsub.'),
@log_calls('AntiSpamPlugin') 'disable_xhtml_muc': (False, ''),
def _nec_atom_entry_received(self, obj): 'disable_xhtml_pm': (False, ''),
if self.config['block_pubsub_messages']: 'block_subscription_requests': (False, ''),
log.info('discarding pubdubd message') 'msgtxt_limit': (-1, ''),
return True 'msgtxt_question': ('Please answer: 12 x 12 =', ''),
'msgtxt_answer': ('', ''),
@log_calls('AntiSpamPlugin') 'antispam_for_conference': (False, ''),
def _nec_decrypted_message_received_received(self, obj): 'conference_white_list': ([], ''), # conference private chat jid's
if self.config['disable_xhtml_muc'] and obj.mtype == 'groupchat': }
self.remove_xhtml(obj)
if self.config['disable_xhtml_pm'] and obj.gc_control and \ # List of outgoing jid's
obj.resource and obj.mtype == 'chat': # Needs to avoid chat of two anti spam plugins
self.remove_xhtml(obj) # Contain all jid's where are you initiate a chat
return False self.outgoing_jids = []
@log_calls('AntiSpamPlugin') @log_calls('AntiSpamPlugin')
def _nec_subscribe_presence_received(self, obj): def _nec_atom_entry_received(self, obj):
if self.config['block_subscription_requests'] and \ if self.config['block_pubsub_messages']:
not gajim.contacts.get_contacts(obj.conn.name, obj.jid): log.info('discarding pubdubd message')
log.info('discarding subscription request from %s' % obj.jid) return True
return True
@log_calls('AntiSpamPlugin')
def remove_xhtml(self, obj): def _nec_message_received_received(self, obj):
html_node = obj.stanza.getTag('html') if self.config['disable_xhtml_muc'] and obj.mtype == 'groupchat':
if html_node: self.remove_xhtml(obj)
obj.stanza.delChild(html_node) if self.config['disable_xhtml_pm'] and obj.gc_control and \
obj.resource and obj.mtype == 'chat':
self.remove_xhtml(obj)
class AntiSpamPluginConfigDialog(GajimPluginConfigDialog): return False
def init(self):
self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path( @log_calls('AntiSpamPlugin')
'config_dialog.ui') def _nec_decrypted_message_received_received(self, obj):
self.xml = Gtk.Builder() if not obj.msgtxt:
self.xml.set_translation_domain('gajim_plugins') return False
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, if self._nec_decrypted_message_received_question(obj):
['anti_spam_config_vbox']) return True
self.config_vbox = self.xml.get_object('anti_spam_config_vbox') limit = self.config['msgtxt_limit']
self.get_child().pack_start(self.config_vbox, True, True, 0) if limit > -1 and len(obj.msgtxt) > limit:
return True
self.block_pubsub_messages_checkbutton = self.xml.get_object( return False
'block_pubsub_messages_checkbutton')
@log_calls('AntiSpamPlugin')
self.xml.connect_signals(self) def _nec_subscribe_presence_received(self, obj):
if self.config['block_subscription_requests'] and \
def on_run(self): not gajim.contacts.get_contacts(obj.conn.name, obj.jid):
self.block_pubsub_messages_checkbutton.set_active(self.plugin.config[ log.info('discarding subscription request from %s' % obj.jid)
'block_pubsub_messages']) return True
widget = self.xml.get_object('disable_xhtml_muc_checkbutton')
widget.set_active(self.plugin.config['disable_xhtml_muc']) @log_calls('AntiSpamPlugin')
widget = self.xml.get_object('disable_xhtml_pm_checkbutton') def _nec_decrypted_message_received_question(self, obj):
widget.set_active(self.plugin.config['disable_xhtml_pm']) if obj.mtype != 'chat' and obj.mtype != 'normal':
widget = self.xml.get_object('block_subscription_requests_checkbutton') return False
widget.set_active(self.plugin.config['block_subscription_requests'])
tjid = obj.jid if obj.mtype == 'normal' else obj.fjid
def on_block_pubsub_messages_checkbutton_toggled(self, button): if tjid in self.outgoing_jids:
self.plugin.config['block_pubsub_messages'] = button.get_active() return False
def on_disable_xhtml_muc_checkbutton_toggled(self, button): answer = self.config['msgtxt_answer']
self.plugin.config['disable_xhtml_muc'] = button.get_active() if len(answer) == 0:
return False
def on_disable_xhtml_pm_checkbutton_toggled(self, button): block_conference = self.config['antispam_for_conference']
self.plugin.config['disable_xhtml_pm'] = button.get_active() is_conference = gajim.contacts.is_gc_contact(obj.conn.name, obj.fjid)
if not block_conference and is_conference:
def on_block_subscription_requests_checkbutton_toggled(self, button): return False
self.plugin.config['block_subscription_requests'] = button.get_active() jid = obj.jid if not is_conference else obj.fjid
# If we receive conference privat message or direct message from unknown user than
# anti spam question will send in background mode, without any notification for us
# There are two methods to see who wrote you and not passed filter:
# 1. Using XML console
# 2. Running Gajim with log info messages and see logs (probably gajim.log file)
if is_conference or not gajim.contacts.get_contacts(obj.conn.name, jid):
if not self.contain_answer(obj.msgtxt, answer):
if is_conference and jid in self.config['conference_white_list']:
return False
self.send_question(obj, jid)
return True
else:
if is_conference and jid not in self.config['conference_white_list']:
self.config['conference_white_list'].append(jid)
# Need to save because 'append' method does not implement __setitem__ method
self.config.save()
return False
@log_calls('AntiSpamPlugin')
def _nec_message_outgoing(self, obj):
if obj.type_ != 'chat' and obj.type_ != 'normal':
return
if isinstance(obj.jid, list):
for i in obj.jid:
if i not in self.outgoing_jids:
self.outgoing_jids.append(i)
else:
if obj.jid not in self.outgoing_jids:
self.outgoing_jids.append(obj.jid)
def send_question(self, obj, jid):
if obj.mtype != 'chat' and obj.mtype != 'normal':
log.info('Anti_spam wrong message type: %s', obj.mtype)
return
# only for 'chat' messages
if obj.receipt_request_tag and obj.mtype == 'chat':
receipt = nbxmpp.Message(to=obj.fjid, typ='chat')
receipt.setTag('received', namespace='urn:xmpp:receipts', attrs={'id': obj.id_})
if obj.thread_id:
receipt.setThread(obj.thread_id)
gajim.connections[obj.conn.name].connection.send(receipt, now=True)
question = self.config['msgtxt_question']
log.info('Anti_spam enabled for %s, question: %s', jid, question)
message = _('Antispam enabled. Please answer the question. The message must only ' + \
'contain the answer. (Messages sent before the correct answer, will be lost): ') \
+ question
if obj.mtype == 'chat':
stanza = nbxmpp.Message(to=jid, body=message, typ=obj.mtype)
else: # for 'normal' type
stanza = nbxmpp.Message(to=jid, body=message, subject='Antispam enabled', typ=obj.mtype)
gajim.connections[obj.conn.name].connection.send(stanza, now=True)
def contain_answer(self, msg, answer):
return answer in msg.split('\n')
def remove_xhtml(self, obj):
html_node = obj.stanza.getTag('html')
if html_node:
obj.stanza.delChild(html_node)
class AntiSpamPluginConfigDialog(GajimPluginConfigDialog):
def init(self):
self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
'config_dialog.ui')
self.xml = Gtk.Builder()
self.xml.set_translation_domain('gajim_plugins')
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH,
['anti_spam_config_vbox'])
self.config_vbox = self.xml.get_object('anti_spam_config_vbox')
self.get_child().pack_start(self.config_vbox, True, True, 0)
self.block_pubsub_messages_checkbutton = self.xml.get_object(
'block_pubsub_messages_checkbutton')
self.xml.connect_signals(self)
def on_run(self):
self.block_pubsub_messages_checkbutton.set_active(self.plugin.config[
'block_pubsub_messages'])
widget = self.xml.get_object('disable_xhtml_muc_checkbutton')
widget.set_active(self.plugin.config['disable_xhtml_muc'])
widget = self.xml.get_object('disable_xhtml_pm_checkbutton')
widget.set_active(self.plugin.config['disable_xhtml_pm'])
widget = self.xml.get_object('block_subscription_requests_checkbutton')
widget.set_active(self.plugin.config['block_subscription_requests'])
widget = self.xml.get_object('message_size_limit_entry')
widget.set_text(str(self.plugin.config['msgtxt_limit']))
widget = self.xml.get_object('antispam_question')
widget.set_text(str(self.plugin.config['msgtxt_question']))
widget = self.xml.get_object('antispam_answer')
widget.set_text(str(self.plugin.config['msgtxt_answer']))
widget = self.xml.get_object('antispam_for_conference')
widget.set_active(self.plugin.config['antispam_for_conference'])
def on_block_pubsub_messages_checkbutton_toggled(self, button):
self.plugin.config['block_pubsub_messages'] = button.get_active()
def on_disable_xhtml_muc_checkbutton_toggled(self, button):
self.plugin.config['disable_xhtml_muc'] = button.get_active()
def on_disable_xhtml_pm_checkbutton_toggled(self, button):
self.plugin.config['disable_xhtml_pm'] = button.get_active()
def on_block_subscription_requests_checkbutton_toggled(self, button):
self.plugin.config['block_subscription_requests'] = button.get_active()
def on_message_size_limit_entry_changed(self, entry):
try:
self.plugin.config['msgtxt_limit'] = int(entry.get_text())
except Exception:
pass
def on_message_question_entry_changed(self, entry):
try:
self.plugin.config['msgtxt_question'] = entry.get_text()
except Exception:
pass
def on_message_answer_entry_changed(self, entry):
try:
self.plugin.config['msgtxt_answer'] = entry.get_text()
except Exception:
pass
def on_antispam_for_conference_checkbutton_toggled(self, button):
self.plugin.config['antispam_for_conference'] = button.get_active()
<?xml version="1.0"?> <?xml version="1.0"?>
<interface> <interface>
<requires lib="gtk+" version="2.16"/> <requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual --> <!-- interface-naming-policy toplevel-contextual -->
<object class="GtkWindow" id="window1"> <object class="GtkWindow" id="window1">
<child> <child>
<object class="GtkVBox" id="anti_spam_config_vbox"> <object class="GtkVBox" id="anti_spam_config_vbox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="border_width">9</property> <property name="border_width">9</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">4</property> <property name="spacing">4</property>
<child> <child>
<object class="GtkCheckButton" id="block_pubsub_messages_checkbutton"> <object class="GtkCheckButton" id="block_pubsub_messages_checkbutton">
<property name="label" translatable="yes">Block pubsub messages</property> <property name="label" translatable="yes">Block pubsub messages</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">If checked, Gajim will block incoming messages from pubsub.</property> <property name="tooltip_text" translatable="yes">If checked, Gajim will block incoming messages from pubsub.</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_block_pubsub_messages_checkbutton_toggled"/> <signal name="toggled" handler="on_block_pubsub_messages_checkbutton_toggled"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="disable_xhtml_muc_checkbutton"> <object class="GtkCheckButton" id="disable_xhtml_muc_checkbutton">
<property name="label" translatable="yes">Disable xhtml in MUCs</property> <property name="label" translatable="yes">Disable xhtml in MUCs</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">If checked, Gajim will remove XHTML from incoming multi-user chat messages</property> <property name="tooltip_text" translatable="yes">If checked, Gajim will remove XHTML from incoming multi-user chat messages</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_disable_xhtml_muc_checkbutton_toggled"/> <signal name="toggled" handler="on_disable_xhtml_muc_checkbutton_toggled"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="disable_xhtml_pm_checkbutton"> <object class="GtkCheckButton" id="disable_xhtml_pm_checkbutton">
<property name="label" translatable="yes">Disable xhtml in private messages</property> <property name="label" translatable="yes">Disable xhtml in private messages</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">If checked, Gajim will remove XHTML from incoming private messages</property> <property name="tooltip_text" translatable="yes">If checked, Gajim will remove XHTML from incoming private messages</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_disable_xhtml_pm_checkbutton_toggled"/> <signal name="toggled" handler="on_disable_xhtml_pm_checkbutton_toggled"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="position">2</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="block_subscription_requests_checkbutton"> <object class="GtkCheckButton" id="block_subscription_requests_checkbutton">
<property name="label" translatable="yes">Block incoming subscription requests</property> <property name="label" translatable="yes">Block incoming subscription requests</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">If checked, Gajim will block incoming subscription requests</property> <property name="tooltip_text" translatable="yes">If checked, Gajim will block incoming subscription requests</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_block_subscription_requests_checkbutton_toggled"/> <signal name="toggled" handler="on_block_subscription_requests_checkbutton_toggled"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="position">3</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
</object> <child>
</child> <object class="GtkHBox" id="box1">
</object> <property name="visible">True</property>
</interface> <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Message size limit:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="message_size_limit_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="changed" handler="on_message_size_limit_entry_changed"/>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_question">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Anti spam question (Leave an empty answer field to disable this function):</property>
<property name="wrap">True</property>
<attributes>
<attribute name="style" value="italic"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="position">6</property>
<property name="padding">3</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="questionbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Question:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="antispam_question">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="changed" handler="on_message_question_entry_changed"/>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Answer:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="antispam_answer">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="changed" handler="on_message_answer_entry_changed"/>
</object>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">7</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="antispam_for_conference">
<property name="label" translatable="yes">Enable for conferences</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">If checked, anti spam question will send for conference private messages</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_antispam_for_conference_checkbutton_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="position">8</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
[info] [info]
name: Anti Spam name: Anti Spam
short_name: anti_spam short_name: anti_spam
version: 0.3.1 version: 1.4.3
description: Block some incoming messages. description: Block some incoming messages.
authors = Yann Leboulanger <asterix@lagaule.org> authors = Yann Leboulanger <asterix@lagaule.org>
Denis Fomin <fominde@gmail.com> Denis Fomin <fominde@gmail.com>
homepage = http://trac-plugins.gajim.org/wiki/AntiSpamPlugin Ilya Kanyukov <ilya.kanukov@gmail.com>
min_gajim_version: 0.16.10 homepage = https://dev.gajim.org/gajim/gajim-plugins/wikis/AntiSpamPlugin
min_gajim_version: 0.16.10
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment