Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gajim/gajim-plugins
  • lovetox/gajim-plugins
  • ag/gajim-plugins
  • FlorianMuenchbach/gajim-plugins
  • rom1dep/gajim-plugins
  • pitchum/gajim-plugins
  • wurstsalat/gajim-plugins
  • Dicson/gajim-plugins
  • andre/gajim-plugins
  • link2xt/gajim-plugins
  • marmistrz/gajim-plugins
  • Jens/gajim-plugins
  • muelli/gajim-plugins
  • asterix/gajim-plugins
  • orhideous/gajim-plugins
  • ngvelprz/gajim-plugins
  • appleorange1/gajim-plugins
  • Martin/gajim-plugins
  • maltel/gajim-plugins
  • Seve/gajim-plugins
  • evert-mouw/gajim-plugins
  • Yuki/gajim-plugins
  • mxre/gajim-plugins
  • ValdikSS/gajim-plugins
  • SaltyBones/gajim-plugins
  • comradekingu/gajim-plugins
  • ritzmann/gajim-plugins
  • genofire/gajim-plugins
  • jjrh/gajim-plugins
  • yarmak/gajim-plugins
  • PapaTutuWawa/gajim-plugins
  • weblate/gajim-plugins
  • XutaxKamay/gajim-plugins
  • nekk/gajim-plugins
  • principis/gajim-plugins
  • cbix/gajim-plugins
  • bodqhrohro/gajim-plugins
  • airtower-luna/gajim-plugins
  • toms/gajim-plugins
  • mesonium/gajim-plugins
  • lissine/gajim-plugins
  • anviar/gajim-plugins
42 results
Show changes
Showing
with 712 additions and 996 deletions
# This file is part of Gajim.
#
# Gajim 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.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
from typing import Any
from typing import cast
from typing import TYPE_CHECKING
from gi.repository import Gtk
from gajim.gtk.const import Setting
from gajim.gtk.const import SettingKind
from gajim.gtk.const import SettingType
from gajim.gtk.settings import SettingsDialog
from gajim.plugins.plugins_i18n import _
if TYPE_CHECKING:
from .anti_spam import AntiSpamPlugin
class AntiSpamConfigDialog(SettingsDialog):
def __init__(self, plugin: AntiSpamPlugin, parent: Gtk.Window) -> None:
self.plugin = plugin
msgtxt_limit = cast(int, self.plugin.config["msgtxt_limit"])
max_length = "" if msgtxt_limit == 0 else msgtxt_limit
settings = [
Setting(
SettingKind.ENTRY,
_("Limit Message Length"),
SettingType.VALUE,
str(max_length),
callback=self._on_length_setting,
data="msgtxt_limit",
desc=_("Limits maximum message length (leave empty to disable)"),
),
Setting(
SettingKind.SWITCH,
_("Deny Subscription Requests"),
SettingType.VALUE,
self.plugin.config["block_subscription_requests"],
callback=self._on_setting,
data="block_subscription_requests",
),
Setting(
SettingKind.SWITCH,
_("Disable XHTML for Group Chats"),
SettingType.VALUE,
self.plugin.config["disable_xhtml_muc"],
callback=self._on_setting,
data="disable_xhtml_muc",
desc=_("Removes XHTML formatting from group chat messages"),
),
Setting(
SettingKind.SWITCH,
_("Disable XHTML for PMs"),
SettingType.VALUE,
self.plugin.config["disable_xhtml_pm"],
callback=self._on_setting,
data="disable_xhtml_pm",
desc=_("Removes XHTML formatting from private messages in group chats"),
),
Setting(
SettingKind.ENTRY,
_("Anti Spam Question"),
SettingType.VALUE,
self.plugin.config["msgtxt_question"],
callback=self._on_setting,
data="msgtxt_question",
desc=_("Question has to be answered in order to contact you"),
),
Setting(
SettingKind.ENTRY,
_("Anti Spam Answer"),
SettingType.VALUE,
self.plugin.config["msgtxt_answer"],
callback=self._on_setting,
data="msgtxt_answer",
desc=_(
"Correct answer to your Anti Spam Question "
"(leave empty to disable question)"
),
),
Setting(
SettingKind.SWITCH,
_("Anti Spam Question in Group Chats"),
SettingType.VALUE,
self.plugin.config["antispam_for_conference"],
callback=self._on_setting,
data="antispam_for_conference",
desc=_(
"Enables anti spam question for private messages in group chats"
),
),
]
SettingsDialog.__init__(
self,
parent,
_("Anti Spam Configuration"),
Gtk.DialogFlags.MODAL,
settings,
"",
)
def _on_setting(self, value: Any, data: Any) -> None:
self.plugin.config[data] = value
def _on_length_setting(self, value: str, data: str) -> None:
try:
self.plugin.config[data] = int(value)
except ValueError:
self.plugin.config[data] = 0
# This file is part of Gajim.
#
# Gajim 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; version 3 only.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
from typing import Any
from typing import cast
from nbxmpp import NodeProcessed
from nbxmpp.protocol import JID
from nbxmpp.protocol import Message
from nbxmpp.protocol import Presence
from nbxmpp.structs import MessageProperties
from nbxmpp.structs import PresenceProperties
from nbxmpp.structs import StanzaHandler
from gajim.common import app
from gajim.common import ged
from gajim.common.client import Client
from gajim.common.events import MessageSent
from gajim.common.modules.base import BaseModule
# Module name
name = "AntiSpam"
zeroconf = False
class AntiSpam(BaseModule):
def __init__(self, client: Client) -> None:
BaseModule.__init__(self, client, plugin=True)
self.handlers = [
StanzaHandler(name="message", callback=self._message_received, priority=48),
StanzaHandler(
name="presence",
callback=self._subscribe_received,
typ="subscribe",
priority=48,
),
]
self.register_events(
[
("message-sent", ged.GUI2, self._on_message_sent),
]
)
for plugin in app.plugin_manager.plugins:
if plugin.manifest.short_name == "anti_spam":
self._config = plugin.config
self._contacted_jids: set[JID] = set()
def _on_message_sent(self, event: MessageSent) -> None:
# We need self._contacted_jids in order to prevent two
# Anti Spam Plugins from chatting with each other.
# This set contains JIDs of all outgoing chats.
self._contacted_jids.add(event.jid)
def _message_received(
self, _con: Client, _stanza: Message, properties: MessageProperties
) -> None:
if properties.is_sent_carbon:
# Another device already sent a message
assert properties.jid
self._contacted_jids.add(properties.jid)
return
msg_body = properties.body
if not msg_body:
return
if self._ask_question(properties):
raise NodeProcessed
msg_from = properties.jid
limit = cast(int, self._config["msgtxt_limit"])
if limit > 0 and len(msg_body) > limit:
self._log.info(
"Discarded message from %s: message length exceeded" % msg_from
)
raise NodeProcessed
if self._config["disable_xhtml_muc"] and properties.type.is_groupchat:
properties.xhtml = None
self._log.info(
"Stripped message from %s: message contained XHTML" % msg_from
)
if self._config["disable_xhtml_pm"] and properties.is_muc_pm:
properties.xhtml = None
self._log.info(
"Stripped message from %s: message contained XHTML" % msg_from
)
def _ask_question(self, properties: MessageProperties) -> bool:
answer = cast(str, self._config["msgtxt_answer"])
if len(answer) == 0:
return False
is_muc_pm = properties.is_muc_pm
if is_muc_pm and not self._config["antispam_for_conference"]:
return False
if properties.type.value not in ("chat", "normal") or properties.is_mam_message:
return False
assert properties.jid
if is_muc_pm:
msg_from = properties.jid
else:
msg_from = JID.from_string(properties.jid.bare)
if msg_from in self._contacted_jids:
return False
# If we receive a PM or a message from an unknown user, our anti spam
# question will silently be sent in the background
whitelist = cast(list[str], self._config["whitelist"])
if str(msg_from) in whitelist:
return False
roster_item = self._client.get_module("Roster").get_item(msg_from)
if is_muc_pm or roster_item is None:
assert properties.body
if answer in properties.body.split("\n"):
if str(msg_from) not in whitelist:
whitelist.append(str(msg_from))
# We need to explicitly save, because 'append' does not
# implement the __setitem__ method
self._config.save()
else:
self._send_question(properties, msg_from)
return True
return False
def _send_question(self, properties: MessageProperties, jid: JID) -> None:
message = "Anti Spam Question: %s" % self._config["msgtxt_question"]
stanza = Message(to=jid, body=message, typ=properties.type.value)
self._client.connection.send_stanza(stanza)
self._log.info("Anti spam question sent to %s", jid)
def _subscribe_received(
self, _con: Client, _stanza: Presence, properties: PresenceProperties
) -> None:
msg_from = properties.jid
assert msg_from is not None
block_sub = self._config["block_subscription_requests"]
roster_item = self._client.get_module("Roster").get_item(msg_from)
if block_sub and roster_item is None:
self._client.get_module("Presence").unsubscribed(msg_from)
self._log.info("Denied subscription request from %s" % msg_from)
raise NodeProcessed
def get_instance(*args: Any, **kwargs: Any) -> tuple[AntiSpam, str]:
return AntiSpam(*args, **kwargs), "AntiSpam"
<?xml version="1.0" encoding="UTF-8"?>
<component type="addon">
<id>org.gajim.Gajim.Plugin.anti_spam</id>
<extends>org.gajim.Gajim</extends>
<name>Anti Spam Plugin</name>
<summary>Block some incoming messages</summary>
<url type="homepage">https://gajim.org/</url>
<metadata_license>CC-BY-SA-3.0</metadata_license>
<project_license>GPL-3.0-only</project_license>
<update_contact>gajim-devel_AT_gajim.org</update_contact>
</component>
{
"authors": [
"Yann Leboulanger <asterix@lagaule.org>",
"Denis Fomin <fominde@gmail.com>",
"Ilya Kanyukov <ilya.kanukov@gmail.com>"
],
"description": "Block some incoming messages.",
"homepage": "https://dev.gajim.org/gajim/gajim-plugins/wikis/AntiSpamPlugin",
"config_dialog": true,
"name": "Anti Spam",
"platforms": [
"others",
"linux",
"darwin",
"win32"
],
"requirements": [
"gajim>=2.0.0"
],
"short_name": "anti_spam",
"version": "1.7.0"
}
\ No newline at end of file
from plugin import BannerTweaksPlugin
<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkWindow" id="window1">
<child>
<object class="GtkVBox" id="banner_tweaks_config_vbox">
<property name="visible">True</property>
<property name="border_width">9</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child>
<object class="GtkCheckButton" id="show_banner_image_checkbutton">
<property name="label" translatable="yes">Display status icon</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">If checked, status icon will be displayed in chat window banner.</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_show_banner_image_checkbutton_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="show_banner_online_msg_checkbutton">
<property name="label" translatable="yes">Display status message of contact</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">If checked, status message of contact will be displayed in chat window banner.</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_show_banner_online_msg_checkbutton_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="show_banner_resource_checkbutton">
<property name="label" translatable="yes">Display resource name of contact</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">If checked, resource name of contact will be displayed in chat window banner.</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_show_banner_resource_checkbutton_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="banner_small_fonts_checkbutton">
<property name="label" translatable="yes">Use small fonts for contact name and resource name</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">If checked, smaller font will be used to display resource name and contact name in chat window banner.</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_banner_small_fonts_checkbutton_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
[info]
name: Banner Tweaks
short_name: banner_tweaks
version: 0.1
description: Allows user to tweak chat window banner appearance (eg. make it compact).
Based on patch by pb in ticket #4133:
http://trac.gajim.org/attachment/ticket/4133.
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it
# -*- coding: utf-8 -*-
## This file is part of Gajim.
##
## Gajim 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; version 3 only.
##
## Gajim 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.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
'''
Adjustable chat window banner.
Includes tweaks to make it compact.
Based on patch by pb in ticket #4133:
http://trac.gajim.org/attachment/ticket/4133/gajim-chatbanneroptions-svn10008.patch
:author: Mateusz Biliński <mateusz@bilinski.it>
:since: 30 July 2008
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
:license: GPL
'''
import sys
import gtk
import gobject
import message_control
from common import gajim
from common import helpers
from plugins import GajimPlugin
from plugins.helpers import log, log_calls
from plugins.gui import GajimPluginConfigDialog
class BannerTweaksPlugin(GajimPlugin):
@log_calls('BannerTweaksPlugin')
def init(self):
self.description = _('Allows user to tweak chat window banner '
'appearance (eg. make it compact).\n'
'Based on patch by pb in ticket #4133:\n'
'http://trac.gajim.org/attachment/ticket/4133.')
self.config_dialog = BannerTweaksPluginConfigDialog(self)
self.gui_extension_points = {
'chat_control_base_draw_banner': (self.chat_control_base_draw_banner_called,
self.chat_control_base_draw_banner_deactivation)
}
self.config_default_values = {
'show_banner_image': (True, 'If True, Gajim will display a status icon in the banner of chat windows.'),
'show_banner_online_msg': (True, 'If True, Gajim will display the status message of the contact in the banner of chat windows.'),
'show_banner_resource': (False, 'If True, Gajim will display the resource name of the contact in the banner of chat windows.'),
'banner_small_fonts': (False, 'If True, Gajim will use small fonts for contact name and resource name in the banner of chat windows.'),
'old_chat_avatar_height': (52, 'chat_avatar_height value before plugin was activated'),
}
@log_calls('BannerTweaksPlugin')
def activate(self):
self.config['old_chat_avatar_height'] = gajim.config.get('chat_avatar_height')
#gajim.config.set('chat_avatar_height', 28)
@log_calls('BannerTweaksPlugin')
def deactivate(self):
gajim.config.set('chat_avatar_height', self.config['old_chat_avatar_height'])
@log_calls('BannerTweaksPlugin')
def chat_control_base_draw_banner_called(self, chat_control):
if not self.config['show_banner_online_msg']:
chat_control.banner_status_label.hide()
chat_control.banner_status_label.set_no_show_all(True)
status_text = ''
chat_control.banner_status_label.set_markup(status_text)
if not self.config['show_banner_image']:
if chat_control.TYPE_ID == message_control.TYPE_GC:
banner_status_img = chat_control.xml.get_object(
'gc_banner_status_image')
else:
banner_status_img = chat_control.xml.get_object(
'banner_status_image')
banner_status_img.clear()
# TODO: part below repeats a lot of code from ChatControl.draw_banner_text()
# This could be rewritten using re module: getting markup text from
# banner_name_label and replacing some elements based on plugin config.
# Would it be faster?
if self.config['show_banner_resource'] or self.config['banner_small_fonts']:
banner_name_label = chat_control.xml.get_object('banner_name_label')
label_text = banner_name_label.get_label()
contact = chat_control.contact
jid = contact.jid
name = contact.get_shown_name()
if chat_control.resource:
name += '/' + chat_control.resource
elif contact.resource and self.config['show_banner_resource']:
name += '/' + contact.resource
if chat_control.TYPE_ID == message_control.TYPE_PM:
name = _('%(nickname)s from group chat %(room_name)s') %\
{'nickname': name, 'room_name': chat_control.room_name}
name = gobject.markup_escape_text(name)
# We know our contacts nick, but if another contact has the same nick
# in another account we need to also display the account.
# except if we are talking to two different resources of the same contact
acct_info = ''
for account in gajim.contacts.get_accounts():
if account == chat_control.account:
continue
if acct_info: # We already found a contact with same nick
break
for jid in gajim.contacts.get_jid_list(account):
other_contact_ = \
gajim.contacts.get_first_contact_from_jid(account, jid)
if other_contact_.get_shown_name() == chat_control.contact.get_shown_name():
acct_info = ' (%s)' % \
gobject.markup_escape_text(chat_control.account)
break
font_attrs, font_attrs_small = chat_control.get_font_attrs()
if self.config['banner_small_fonts']:
font_attrs = font_attrs_small
st = gajim.config.get('displayed_chat_state_notifications')
cs = contact.chatstate
if cs and st in ('composing_only', 'all'):
if contact.show == 'offline':
chatstate = ''
elif st == 'all' or cs == 'composing':
chatstate = helpers.get_uf_chatstate(cs)
else:
chatstate = ''
label_text = '<span %s>%s</span><span %s>%s %s</span>' % \
(font_attrs, name, font_attrs_small, acct_info, chatstate)
else:
# weight="heavy" size="x-large"
label_text = '<span %s>%s</span><span %s>%s</span>' % \
(font_attrs, name, font_attrs_small, acct_info)
banner_name_label.set_markup(label_text)
@log_calls('BannerTweaksPlugin')
def chat_control_base_draw_banner_deactivation(self, chat_control):
pass
#chat_control.draw_banner()
class BannerTweaksPluginConfigDialog(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,
['banner_tweaks_config_vbox'])
self.config_vbox = self.xml.get_object('banner_tweaks_config_vbox')
self.child.pack_start(self.config_vbox)
self.show_banner_image_checkbutton = self.xml.get_object('show_banner_image_checkbutton')
self.show_banner_online_msg_checkbutton = self.xml.get_object('show_banner_online_msg_checkbutton')
self.show_banner_resource_checkbutton = self.xml.get_object('show_banner_resource_checkbutton')
self.banner_small_fonts_checkbutton = self.xml.get_object('banner_small_fonts_checkbutton')
self.xml.connect_signals(self)
def on_run(self):
self.show_banner_image_checkbutton.set_active(self.plugin.config['show_banner_image'])
self.show_banner_online_msg_checkbutton.set_active(self.plugin.config['show_banner_online_msg'])
self.show_banner_resource_checkbutton.set_active(self.plugin.config['show_banner_resource'])
self.banner_small_fonts_checkbutton.set_active(self.plugin.config['banner_small_fonts'])
def on_show_banner_image_checkbutton_toggled(self, button):
self.plugin.config['show_banner_image'] = button.get_active()
def on_show_banner_online_msg_checkbutton_toggled(self, button):
self.plugin.config['show_banner_online_msg'] = button.get_active()
def on_show_banner_resource_checkbutton_toggled(self, button):
self.plugin.config['show_banner_resource'] = button.get_active()
def on_banner_small_fonts_checkbutton_toggled(self, button):
self.plugin.config['banner_small_fonts'] = button.get_active()
from chatstate import ChatstatePlugin
# -*- coding: utf-8 -*-
##
import gobject
from plugins import GajimPlugin
from plugins.helpers import log_calls
from common import ged
from common import gajim
from common import helpers
import gtkgui_helpers
import unicodedata
def paragraph_direction_mark(text):
"""
Determine paragraph writing direction according to
http://www.unicode.org/reports/tr9/#The_Paragraph_Level
Returns either Unicode LTR mark or RTL mark.
"""
for char in text:
bidi = unicodedata.bidirectional(char)
if bidi == 'L':
return u'\u200E'
elif bidi == 'AL' or bidi == 'R':
return u'\u200F'
return u'\u200E'
class ChatstatePlugin(GajimPlugin):
@log_calls('ChatstatePlugin')
def init(self):
self.description = _('Chat State Notifications in roster.'
'Font color of the contact varies depending on the chat state.\n'
'The plugin does not work if you use custom font color for contacts in roster.\n'
'http://trac.gajim.org/ticket/3628.\nhttp://xmpp.org/extensions/xep-0085.html')
self.config_dialog = None # ChatstatePluginConfigDialog(self)
self.events_handlers = {'chatstate-received':
(ged.GUI2, self.chatstate_received), }
self.active = None
def chatstate_received(self, obj):
if not self.active:
return
contact = gajim.contacts.get_contact_from_full_jid(obj.conn.name,
obj.fjid)
if not contact:
return
chatstate = obj.chatstate
if chatstate not in self.chatstates.keys():
return
self.model = gajim.interface.roster.model
child_iters = gajim.interface.roster._get_contact_iter(obj.jid,
obj.conn.name, contact, self.model)
name = gobject.markup_escape_text(contact.get_shown_name())
contact_instances = gajim.contacts.get_contacts(obj.conn.name,
contact.jid)
# Show resource counter
nb_connected_contact = 0
for c in contact_instances:
if c.show not in ('error', 'offline'):
nb_connected_contact += 1
if nb_connected_contact > 1:
name += paragraph_direction_mark(unicode(name))
name += u' (%d)' % nb_connected_contact
for child_iter in child_iters:
if chatstate != 'gone':
color = self.chatstates[chatstate]
name = '<span foreground="%s">%s</span>' % (color, name)
if contact.status and gajim.config.get(
'show_status_msgs_in_roster'):
status = contact.status.strip()
if status != '':
status = helpers.reduce_chars_newlines(status,
max_lines=1)
name += '\n<span size="small" style="italic" ' \
'foreground="%s">%s</span>' % (self.status_color,
gobject.markup_escape_text(status))
self.model[child_iter][1] = name
@log_calls('ChatstatePlugin')
def activate(self):
color = gtkgui_helpers.get_fade_color(gajim.interface.roster.tree,
False, False)
self.status_color = '#%04x%04x%04x' % (color.red, color.green,
color.blue)
theme = gajim.config.get('roster_theme')
self.chatstates = {'active': gajim.config.get('inmsgcolor'),
'composing': gajim.config.get_per('themes', theme,
'state_composing_color'),
'inactive': gajim.config.get_per('themes', theme,
'state_inactive_color'),
'paused': gajim.config.get_per('themes', theme,
'state_paused_color'),
'gone': None, }
self.active = True
@log_calls('ChatstatePlugin')
def deactivate(self):
self.active = False
[info]
name: Chatstate in roster
short_name: chatstate
version: 0.5
description: Chat State Notifications in roster.
Font color of the contact varies depending on the chat state.
The plugin does not work if you use custom font color for contacts in roster.
http://trac.gajim.org/ticket/3628.
http://xmpp.org/extensions/xep-0085.html
authors = Denis Fomin <fominde@gmail.com>
homepage = http://trac-plugins.gajim.org/wiki/ChatstatePlugin
from clickable_nicknames import ClickableNicknames
# -*- coding: utf-8 -*-
import gtk
from string import rstrip
from string import lstrip
from common import gajim
from plugins import GajimPlugin
from plugins.helpers import log_calls, log
class ClickableNicknames(GajimPlugin):
@log_calls('ClickableNicknamesPlugin')
def init(self):
self.description = _('Clickable nicknames '
'in the conversation textview.')
self.config_dialog = None # ClickableNicknamesPluginConfigDialog(self)
self.gui_extension_points = {
'chat_control_base': (self.connect_with_chat_control,
self.disconnect_from_chat_control)}
self.is_active = None
self.gc_controls = {}
self.tag_names = []
colors = gajim.config.get('gc_nicknames_colors')
colors = colors.split(':')
for i, color in enumerate(colors):
tagname = 'gc_nickname_color_' + str(i)
self.tag_names.append(tagname)
@log_calls('ClickableNicknamesPlugin')
def activate(self):
for gc_control in gajim.interface.msg_win_mgr.get_controls('gc'):
# TODO support minimized groupchat
if gc_control not in self.gc_controls.keys():
control = Base(self, gc_control)
self.gc_controls[gc_control] = control
else:
self.gc_controls[gc_control].connect_signals()
self.is_active = True
@log_calls('ClickableNicknamesPlugin')
def deactivate(self):
for control in self.gc_controls.keys():
self.gc_controls[control].disconnect_from_chat_control()
self.gc_controls.clear()
self.is_active = None
@log_calls('ClickableNicknamesPlugin')
def connect_with_chat_control(self, chat_control):
if chat_control.widget_name != 'groupchat_control':
return
if self.is_active:
control = Base(self, chat_control)
self.gc_controls[chat_control] = control
@log_calls('ClickableNicknamesPlugin')
def disconnect_from_chat_control(self, chat_control):
pass
class Base(object):
def __init__(self, plugin, chat_control):
self.plugin = plugin
self.chat_control = chat_control
self.textview = self.chat_control.conv_textview
self.tags_id = []
self.change_cursor = False
self.connect_signals()
def connect_signals(self):
# connect signals with textbuffer tags
self.tag_names = self.plugin.tag_names
tag_table = self.textview.tv.get_buffer().get_tag_table()
for name in self.tag_names:
tag = tag_table.lookup(name)
if tag:
id_ = tag.connect('event', self.insert_nick, name)
self.chat_control.handlers[id_] = tag
self.tags_id.append((id_, tag))
self.id_ = self.textview.tv.connect('motion_notify_event',
self.on_textview_motion_notify_event)
self.chat_control.handlers[self.id_] = self.textview.tv
def on_textview_motion_notify_event(self, widget, event):
# change cursor on the nicks
pointer_x, pointer_y = self.textview.tv.window.get_pointer()[0:2]
x, y = self.textview.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT,
pointer_x, pointer_y)
tags = self.textview.tv.get_iter_at_location(x, y).get_tags()
tag_table = self.textview.tv.get_buffer().get_tag_table()
if self.change_cursor:
self.textview.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
gtk.gdk.Cursor(gtk.gdk.XTERM))
self.change_cursor = False
for tag in tags:
if tag in [x[1] for x in self.tags_id]:
self.textview.tv.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
gtk.gdk.Cursor(gtk.gdk.HAND2))
self.change_cursor = True
self.textview.on_textview_motion_notify_event(widget, event)
def insert_nick(self, texttag, widget, event, iter_, kind):
# insert nickname to message buffer
if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1:
# left mouse button clicked
begin_iter = iter_.copy()
# we get the begining of the tag
while not begin_iter.begins_tag(texttag):
begin_iter.backward_char()
end_iter = iter_.copy()
# we get the end of the tag
while not end_iter.ends_tag(texttag):
end_iter.forward_char()
buffer_ = self.textview.tv.get_buffer()
word = buffer_.get_text(begin_iter, end_iter).decode('utf-8')
nick = word.rstrip().rstrip(gajim.config.get('after_nickname'))
if nick.startswith('* '):
nick = nick.lstrip('* ').split(' ')[0]
nick = nick.lstrip(gajim.config.get('before_nickname'))
nick = nick + gajim.config.get('gc_refer_to_nick_char') + ' '
message_buffer = self.chat_control.msg_textview.get_buffer()
message_buffer.insert_at_cursor(nick)
self.chat_control.msg_textview.grab_focus()
def disconnect_from_chat_control(self):
# disconnect signals from textbuffer tags
for item in self.tags_id:
if item[1].handler_is_connected(item[0]):
item[1].disconnect(item[0])
if self.textview.tv.handler_is_connected(self.id_):
self.textview.tv.disconnect(self.id_)
[info]
name: Clickable Nicknames
short_name: clickable_nicknames
version: 0.1
description: Clickable nicknames in the conversation textview.
authors: Denis Fomin <fominde@gmail.com>
homepage: http://trac-plugins.gajim.org/wiki/ClickableNicknamesPlugin
# Updating icons
After adding an icon, `icon-theme.cache` has to be updated:
`cd ./hicolor`
`gtk-update-icon-cache .`
from clients_icons import ClientsIconsPlugin
from .clients_icons import ClientsIconsPlugin # pyright: ignore # noqa: F401
# This file is part of Gajim.
#
# Gajim 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.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
from typing import Any
from collections import UserDict
from dataclasses import dataclass
from gajim.plugins.plugins_i18n import _
@dataclass
class ClientData:
default: tuple[str, str] | None = None
variations: dict[str, str] | None = None
def get_variations(client_name: str | None) -> list[str]:
# get_variations('Conversation Legacy 1.2.3')
#
# Returns List:
# [Conversation Legacy 1.2.3,
# Conversation Legacy
# Conversation]
if client_name is None:
return []
alts = client_name.split()
alts = [" ".join(alts[: (i + 1)]) for i in range(len(alts))]
alts.reverse()
return alts
class ClientsDict(UserDict[str, ClientData]):
def get_client_data(self, name: str, node: str) -> tuple[str, str]:
client_data = self.get(node)
if client_data is None:
return _("Unknown"), "xmpp-client-unknown"
if client_data.variations is None:
assert client_data.default is not None
client_name, icon_name = client_data.default
return client_name, f"xmpp-client-{icon_name}"
variations = get_variations(name)
for var in variations:
try:
return var, f"xmpp-client-{client_data.variations[var]}"
except KeyError:
pass
assert client_data.default is not None
client_name, icon_name = client_data.default
return client_name, f"xmpp-client-{icon_name}"
# ClientData(
# default=(Shown name, icon name)
# variations={Shown name, icon name}
# )
# pylint: disable=too-many-lines
CLIENTS = ClientsDict(
{
"http://gajim.org": ClientData(("Gajim", "gajim")),
"https://gajim.org": ClientData(("Gajim", "gajim")),
"http://conversations.im": ClientData(
default=("Conversations", "conversations"),
variations={"Conversations Legacy": "conversations-legacy"},
),
"http://jabber.pix-art.de": ClientData(("Pix-Art Messenger", "pixart")),
"http://blabber.im": ClientData(("blabber.im", "blabber")),
"http://monocles.de": ClientData(("monocles chat", "monocles-chat")),
"http://pidgin.im/": ClientData(("Pidgin", "pidgin")),
"https://poez.io": ClientData(("Poezio", "poezio")),
"https://yaxim.org/": ClientData(("yaxim", "yaxim")),
"https://yaxim.org/bruno/": ClientData(("Bruno", "bruno")),
"http://mcabber.com/caps": ClientData(("MCabber", "mcabber")),
"http://psi-plus.com": ClientData(("Psi+", "psiplus")),
"https://psi-plus.com": ClientData(("Psi+", "psiplus")),
"https://dino.im": ClientData(("Dino", "dino")),
"http://monal.im/": ClientData(("Monal", "monal")),
"http://slixmpp.com/ver/1.2.4": ClientData(("Bot", "bot")),
"http://slixmpp.com/ver/1.3.0": ClientData(("Bot", "bot")),
"https://www.xabber.com/": ClientData(("Xabber", "xabber")),
"http://www.profanity.im": ClientData(("Profanity", "profanity")),
"http://swift.im": ClientData(("Swift", "swift")),
"https://salut-a-toi.org": ClientData(("Salut à Toi", "sat")),
"https://conversejs.org": ClientData(("Converse", "conversejs")),
"http://bitlbee.org/xmpp/caps": ClientData(("BitlBee", "bitlbee")),
"http://tkabber.jabber.ru/": ClientData(("Tkabber", "tkabber")),
"http://miranda-ng.org/caps": ClientData(("Miranda NG", "miranda_ng")),
"http://www.adium.im/": ClientData(("Adium", "adium")),
"http://www.adiumx.com/caps": ClientData(("Adium", "adium")),
"http://www.adiumx.com": ClientData(("Adium", "adium")),
"http://aqq.eu/": ClientData(("Aqq", "aqq")),
"http://www.asterisk.org/xmpp/client/caps": ClientData(
("Asterisk", "asterisk")
),
"http://ayttm.souceforge.net/caps": ClientData(("Ayttm", "ayttm")),
"http://www.barobin.com/caps": ClientData(("Bayanicq", "bayanicq")),
"http://simpleapps.ru/caps#blacksmith": ClientData(("Blacksmith", "bot")),
"http://blacksmith-2.googlecode.com/svn/": ClientData(("Blacksmith-2", "bot")),
"http://coccinella.sourceforge.net/protocol/caps": ClientData(
("Coccinella", "coccinella")
),
"http://digsby.com/caps": ClientData(("Digsby", "digsby")),
"http://emacs-jabber.sourceforge.net": ClientData(
("Emacs Jabber Client", "emacs")
),
"http://emess.eqx.su/caps": ClientData(("Emess", "emess")),
"http://live.gnome.org/empathy/caps": ClientData(
("Empathy", "telepathy.freedesktop.org")
),
"http://eqo.com/": ClientData(("Eqo", "libpurple")),
"http://exodus.jabberstudio.org/caps": ClientData(("Exodus", "exodus")),
"http://fatal-bot.spb.ru/caps": ClientData(("Fatal-bot", "bot")),
"http://svn.posix.ru/fatal-bot/trunk": ClientData(("Fatal-bot", "bot")),
"http://isida.googlecode.com": ClientData(("Isida", "isida-bot")),
"http://isida-bot.com": ClientData(("Isida", "isida-bot")),
"http://jabga.ru": ClientData(("Fin jabber", "fin")),
"http://chat.freize.org/caps": ClientData(("Freize", "freize")),
"http://gabber.sourceforge.net": ClientData(("Gabber", "gabber")),
"http://glu.net/": ClientData(("Glu", "glu")),
"http://mail.google.com/xmpp/client/caps": ClientData(("GMail", "google.com")),
"http://www.android.com/gtalk/client/caps": ClientData(
("GTalk", "talk.google.com")
),
"talk.google.com": ClientData(("GTalk", "talk.google.com")),
"http://talkgadget.google.com/client/caps": ClientData(("GTalk", "google")),
"http://talk.google.com/xmpp/bot/caps": ClientData(("GTalk", "google")),
"http://aspro.users.ru/historian-bot/": ClientData(("Historian-bot", "bot")),
"http://www.apple.com/ichat/caps": ClientData(("IChat", "ichat")),
"http://instantbird.com/": ClientData(("Instantbird", "instantbird")),
"http://j-tmb.ru/caps": ClientData(("J-tmb", "bot")),
"http://jabbroid.akuz.de": ClientData(("Jabbroid", "android")),
"http://jabbroid.akuz.de/caps": ClientData(("Jabbroid", "android")),
"http://dev.jabbim.cz/jabbim/caps": ClientData(("Jabbim", "jabbim")),
"http://jabbrik.ru/caps": ClientData(("Jabbrik", "bot")),
"http://jabrvista.net.ru": ClientData(("Jabvista", "bot")),
"http://jajc.jrudevels.org/caps": ClientData(("JAJC", "jajc")),
"http://qabber.ru/jame-bot": ClientData(("Jame-bot", "bot")),
"https://www.jappix.com/": ClientData(("Jappix", "jappix")),
"http://japyt.googlecode.com": ClientData(("Japyt", "japyt")),
"http://jasmineicq.ru/caps": ClientData(("Jasmine", "jasmine")),
"http://jimm.net.ru/caps": ClientData(("Jimm", "jimm-aspro")),
"http://jitsi.org": ClientData(("Jitsi", "jitsi")),
"http://jtalk.ustyugov.net/caps": ClientData(("Jtalk", "jtalk")),
"http://pjc.googlecode.com/caps": ClientData(("Jubo", "jubo")),
"http://juick.com/caps": ClientData(("Juick", "juick")),
"http://kopete.kde.org/jabber/caps": ClientData(("Kopete", "kopete")),
"http://bluendo.com/protocol/caps": ClientData(("Lampiro", "lampiro")),
"http://lytgeygen.ru/caps": ClientData(("Lytgeygen", "bot")),
"http://agent.mail.ru/caps": ClientData(("Mailruagent", "mailruagent")),
"http://agent.mail.ru/": ClientData(("Mailruagent", "mailruagent")),
"http://tomclaw.com/mandarin_im/caps": ClientData(("Mandarin", "mandarin")),
"http://mchat.mgslab.com/": ClientData(("Mchat", "mchat")),
"https://www.meebo.com/": ClientData(("Meebo", "meebo")),
"http://megafonvolga.ru/": ClientData(("Megafon", "megafon")),
"http://miranda-im.org/caps": ClientData(("Miranda", "miranda")),
"https://movim.eu/": ClientData(("Movim", "movim")),
"http://moxl.movim.eu/": ClientData(("Movim", "movim")),
"nimbuzz:caps": ClientData(("Nimbuzz", "nimbuzz")),
"http://nimbuzz.com/caps": ClientData(("Nimbuzz", "nimbuzz")),
"http://home.gna.org/": ClientData(("Omnipresence", "omnipresence")),
"http://oneteam.im/caps": ClientData(("OneTeam", "oneteamiphone")),
"http://www.process-one.net/en/solutions/oneteam_iphone/": ClientData(
("OneTeam-IPhone", "oneteamiphone")
),
"rss@isida-bot.com": ClientData(("Osiris", "osiris")),
"http://chat.ovi.com/caps": ClientData(("Ovi-chat", "ovi-chat")),
"http://opensource.palm.com/packages.html": ClientData(("Palm", "palm")),
"http://palringo.com/caps": ClientData(("Palringo", "palringo")),
"http://pandion.im/": ClientData(("Pandion", "pandion")),
"http://pigeon.vpro.ru/caps": ClientData(("Pigeon", "pigeon")),
"psto@psto.net": ClientData(("Psto", "psto")),
"http://qq-im.com/caps": ClientData(("QQ", "qq")),
"http://qq.com/caps": ClientData(("QQ", "qq")),
"http://2010.qip.ru/caps": ClientData(("Qip", "qip")),
"http://qip.ru/caps": ClientData(("Qip", "qip")),
"http://qip.ru/caps?QIP": ClientData(("Qip", "qip")),
"http://pda.qip.ru/caps": ClientData(("Qip-PDA", "qippda")),
"http://qutim.org": ClientData(("QutIM", "qutim")),
"http://qutim.org/": ClientData(("QutIM", "qutim")),
"http://apps.radio-t.com/caps": ClientData(("Radio-t", "radio-t")),
"http://sim-im.org/caps": ClientData(("Sim", "sim")),
"http://www.lonelycatgames.com/slick/caps": ClientData(("Slick", "slick")),
"http://snapi-bot.googlecode.com/caps": ClientData(("Snapi-bot", "bot")),
"http://www.igniterealtime.org/project/spark/caps": ClientData(
("Spark", "spark")
),
"http://spectrum.im/": ClientData(("Spectrum", "spectrum")),
"http://storm-bot.googlecode.com/svn/trunk": ClientData(("Storm-bot", "bot")),
"http://jabber-net.ru/caps/talisman-bot": ClientData(("Talisman-bot", "bot")),
"http://jabber-net.ru/talisman-bot/caps": ClientData(("Talisman-bot", "bot")),
"http://www.google.com/xmpp/client/caps": ClientData(
("Talkonaut", "talkonaut")
),
"http://telepathy.freedesktop.org/caps": ClientData(
("SlicTelepathyk", "telepathy.freedesktop.org")
),
"http://tigase.org/messenger": ClientData(("Tigase", "tigase")),
"http://trillian.im/caps": ClientData(("Trillian", "trillian")),
"http://vacuum-im.googlecode.com": ClientData(("Vacuum", "vacuum")),
"http://code.google.com/p/vacuum-im/": ClientData(("Vacuum", "vacuum")),
"http://witcher-team.ucoz.ru/": ClientData(("Witcher", "bot")),
"http://online.yandex.ru/caps": ClientData(("Yaonline", "yaonline")),
"http://www.igniterealtime.org/projects/smack/": ClientData(
("Xabber", "xabber")
),
"http://www.xfire.com/": ClientData(("Xfire", "xfire")),
"http://www.xfire.com/caps": ClientData(("Xfire", "xfire")),
"http://xu-6.jabbrik.ru/caps": ClientData(("XU-6", "bot")),
}
)
# pylint: enable=too-many-lines
def get_data(*args: Any) -> tuple[str, str]:
return CLIENTS.get_client_data(*args)
clients_icons/clients_icons.png

558 B

This diff is collapsed.
# This file is part of Gajim.
#
# Gajim 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.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
from typing import Any
from typing import TYPE_CHECKING
from gi.repository import Gtk
from gajim.gtk.const import Setting
from gajim.gtk.const import SettingKind
from gajim.gtk.const import SettingType
from gajim.gtk.settings import SettingsDialog
from gajim.plugins.plugins_i18n import _
if TYPE_CHECKING:
from .clients_icons import ClientsIconsPlugin
class ClientsIconsConfigDialog(SettingsDialog):
def __init__(self, plugin: ClientsIconsPlugin, parent: Gtk.Window) -> None:
self.plugin = plugin
settings = [
Setting(
SettingKind.SWITCH,
_("Show Icon for Unknown Clients"),
SettingType.VALUE,
self.plugin.config["show_unknown_icon"],
callback=self._on_setting,
data="show_unknown_icon",
),
]
SettingsDialog.__init__(
self,
parent,
_("Clients Icons Configuration"),
Gtk.DialogFlags.MODAL,
settings,
"",
)
def _on_setting(self, value: Any, data: Any) -> None:
self.plugin.config[data] = value