...
 
Commits (148)
syntax: glob
*.pyc
*.pyo
__pycache__/
\ No newline at end of file
__pycache__/
*.*~
\ No newline at end of file
## Welcome to the Gajim Plugins Wiki
Here are some plugins that are written for <a href="https://gajim.org">Gajim</a> by the community. Report problems about those plugins here.
In this place you find all plugins that are written for <a href="https://gajim.org">Gajim</a> by the community. If you experience any problems with those plugins, please report them here.
## How to install
## How to install plugins
There are several ways to install a plugin:
- You can browse / download / enable / configure plugins in Gajim, Edit menu -> Plugins.
- You can clone the repository directly from [here](https://dev.gajim.org/gajim/gajim-plugins) and copy it to
- You can browse / download / enable / configure plugins from within Gajim via 'Gajim' > 'Plugins' menu.
- You can also clone the repository directly from our Git and copy it to:
**Linux:** `~/.local/share/gajim/plugins/`
**Linux:** ~/.local/share/gajim/plugins/
**Windows:** `C:\Users\USERNAME\AppData\Roaming\Gajim\Plugins`
**Windows:** C:\Users\USERNAME\AppData\Roaming\Gajim\Plugins
- Alternatively (for developing), you can also symlink the gajim-plugins repository to Gajim's plugin path
**Symlink:** `ln -s /path/to/gajim-plugins-repository/* ~/.local/share/gajim/plugins/`
**For each major Gajim version there is a different plugins branch:**
| Version | Plugins branch |
| ------- | -------------- |
|Gajim 1.0|[1.0 branch](https://dev.gajim.org/gajim/gajim-plugins/tree/gajim_1.0)|
|Gajim 1.1|[1.1 branch](https://dev.gajim.org/gajim/gajim-plugins/tree/gajim_1.1)|
|Gajim master|[master branch](https://dev.gajim.org/gajim/gajim-plugins/tree/master)|
*Note: Using master branch for plugins requires frequent updates of both Gajim and plugins!*
## Share / Improve Plugins
## Share / improve Plugins
You have written a new plugin or want to improve an already existing one?
First, Thanks for that! Here is how to do that:
First, thanks for that! Here is how to start:
- Register an account [here](https://dev.gajim.org/users/sign_in)
- Tell us about your plans at gajim@conference.gajim.org (we need to set your permission on Gitlab)
- Register an account on our Gitlab [here](https://dev.gajim.org/users/sign_in)
- Tell us about your plans at [gajim@conference.gajim.org](xmpp:gajim@conference.gajim.org?join)
- Fork the Gajim-Plugins [repository](https://dev.gajim.org/gajim/gajim-plugins)
- When you are finished make a pull request against the main repository
- When you are finished, make a pull request against the main repository. You can read about how to use git [here](https://dev.gajim.org/gajim/gajim/wikis/howtogit).
- Additionally, there is a list of [plugin events](https://dev.gajim.org/gajim/gajim/wikis/development/pluginsevents) which might be helpful
**Before you put in any work, please contact us on gajim@conference.gajim.org**
**Before you put in any work, please contact us on [gajim@conference.gajim.org](xmpp:gajim@conference.gajim.org?join)**
**Dont use dev.gajim.org for any projects that are not directly for the benefit of Gajim**
## Plugins list
* [AntiSpamPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/AntiSpamPlugin) Block some incoming messages
* [AppindicatorSupportPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/AppindicatorSupportPlugin) Plugin that add indicator applet support to gajim
* [BannerTweaksPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/BannerTweaksPlugin) Ability to configure the banner in chat windows
* [BirthdayReminderPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/BirthdayReminderPlugin) Birthday reminder
* [ChatstatePlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/ChatstatePlugin) Chat State Notifications in roster.
* [ClickableNicknamesPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/ClickableNicknamesPlugin) Click the left mouse button on a nickname in a groupchat conversation to insert the nickname in the input field.
* [ClientsIconsPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/ClientsIconsPlugin) Shows the clients icons in the roster and in groupchats.
* [EmoticonPackPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/EmoticonPackPlugin) A pack of emoticon themes
* [FileSharing](https://dev.gajim.org/gajim/gajim-plugins/wikis/FileSharing) Allows you to share folders with your peers using jingle file transfer.
* [FlashingKeyboardPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/FlashingKeyboardPlugin) Make keyboard flash when we get a new message.
* [GnomeSessionManagerPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/GnomeSessionManagerPlugin) set and react on GNOME SessionManager presence settings.
* [GoogleTranslationPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/GoogleTranslationPlugin) Automatically translate incoming messages.
* [HamsterIntegration](https://dev.gajim.org/gajim/gajim-plugins/wikis/HamsterIntegration) Integration with project hamster.
* [HttpUploadPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/HttpUploadPlugin) Share files with offline users, multi client users and even in MUCs with [XEP-0363](http://xmpp.org/extensions/xep-0363.html)
* [ImagePlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/ImagePlugin) This plugin is designed to send a small(0 - 40 kb) graphic image to your contact.
* [JuickPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/JuickPlugin) More comfortable use of juick.com (microblogging service).
* [LatexPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/LatexPlugin) Render latex expressions.
* [LengthNotifierPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/LengthNotifierPlugin) Be notified when message length reaches a limit.
* [MessageBoxSizePlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/MessageBoxSizePlugin) Allows you to adjust the height of the new message input field.
* [MprisSupportPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/MprisSupportPlugin) MPRIS2 support.
* [NowListenPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/NowListenPlugin) Copy tune info to conversation input box.
* [OffTheRecordPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/OffTheRecordPlugin) Provides protocol independent encryption (see https://otr.cypherpunks.ca for more information).
* [OfflineBookmarksPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/OfflineBookmarksPlugin) Save bookmarks offline inside the plugin configuration file.
* [PluginInstallerPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/PluginInstallerPlugin) install new plugins in one click.
* [PluginsTranslationsPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/PluginsTranslationsPlugin) This plugin contains translations files for Gajim plugins.
* [QuickRepliesPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/QuickRepliesPlugin) Plugin for quick replies.
* [RegexFilterPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/RegexFilterPlugin) Regex filtering of incoming messages.
* [RosterTweaksPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/RosterTweaksPlugin) Allows user to tweak roster window appearance.
* [ServerStatusIconsPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/ServerStatusIconsPlugin) Replace standard Gajim status icons with server specific for known XMPP server accounts.
* [SetLocationPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/SetLocationPlugin) Allows you to manually specify your geographical location.
* [SnarlNotifications](https://dev.gajim.org/gajim/gajim-plugins/wikis/SnarlNotifications) Shows events notification using [Snarl](http://snarl.fullphat.net/) under Microsoft Windows.
* [SyntaxHighlightPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/syntaxhighlightplugin) Highlights Code in the Chat Window for many languages.
* [TictactoePlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/TictactoePlugin) Play Tic tac toe with your contacts.
* [TriggersPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/TriggersPlugin) Configure Gajim's behaviour when receiving some events.
* [ThemeSwitcherPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/ThemeSwitcherPlugin) Change the active GTK+ theme.
* [UbuntuIntegrationPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/UbuntuIntegrationPlugin) The plugin integrates Gajim with the Ubuntu Messaging Menu and the Me Menu.
* [UrlImagePreviewPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/UrlImagePreviewPlugin) Url image preview in chatbox.
* [UrlShortenerPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/UrlShortenerPlugin) Allows users to shorten a long URL.
* [WhiteboardPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/WhiteboardPlugin) Ability to share a whiteboard with a contact
* [WicdSupportPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/WicdSupportPlugin) Support for autodetection of network status for Wicd Network Manager.
* [WrongLayoutPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/WrongLayoutPlugin) Press alt+r to convert chars typed in wrong layout( Rus<>Eng).
* [OmemoGajimPlugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/OmemoGajimPlugin) Experimental plugin for the OMEMO Multi-End Message and Object Encryption.
\ No newline at end of file
All available plugins are listed [here](https://dev.gajim.org/gajim/gajim-plugins/wikis/home).
{"afaik": "as far as I know",
"afaict": "as far as I can tell",
"afk": "away from keyboard",
"atm": "at the moment",
"bbiab": "be back in a bit",
"bbiaf": "be back in a few (minutes)",
"bbl": "be back later",
"bbs": "be back soon",
"b/c": "because",
"bf": "boyfriend",
"bfo": "blinding flash of the obvious",
"brb": "be right back",
"bsod": "blue screen of death",
"btw": "by the way",
"ciao": "Italian for goodbye",
"ctrn": "can't talk right now",
"cul8r": "see you later",
"cya": "see ya",
"dhtb": "don't have the bandwidth",
"f2f": "face to face",
"fubar": "fucked up beyond all recognition",
"fwiw": "for what it's worth",
"fyi": "for your information",
"gmta": "great minds think alike",
"iam": "in a meeting",
"ianal": "I am not a lawyer",
"ihmb": "I hate my boss",
"iirc": "if I recall correctly",
"imho": "in my humble opinion",
"imo": "in my opinion",
"iow": "in other words",
"irl": "in real life",
"<g>": "grin",
"*g*": "grin",
"gf": "girlfriend",
"gmta": "great minds think alike",
"g2g": "got to go",
"jid": "jabber identifier",
"j/k": "just kidding",
"ok": "okay",
"lol": "laugh out loud",
"l8r": "later",
"msg": "message",
"n/m": "never mind",
"n/p": "no problem",
"oAo": "over and out!",
"omg": "oh my god",
"oob": "out of band",
"otoh": "on the other hand",
"oww": "oops, wrong window!",
"otp": "on the phone",
"pita": "pain in the ass",
"pov": "point of view",
"pw": "password",
"rotfl": "rolling on the floor laughing",
"rsn": "real soon now",
"rtfm": "read the friendly manual",
"slap": "sounds like a plan",
"thx": "thanks",
"tia": "thanks in advance",
"tla": "three-letter arconym",
"ttfn": "ta ta for now",
"ttyl": "talk to you later",
"wb": "welcome back",
"wfm": "works for me",
"wtf": "what the fuck?!",
"wtg": "way to go!",
"xfer": "transfer",
"ymmv": "your mileage may vary",}
DEFAULT_DATA = {
"afaik": "as far as I know",
"afaict": "as far as I can tell",
"afk": "away from keyboard",
"atm": "at the moment",
"bbiab": "be back in a bit",
"bbiaf": "be back in a few (minutes)",
"bbl": "be back later",
"bbs": "be back soon",
"b/c": "because",
"bf": "boyfriend",
"bfo": "blinding flash of the obvious",
"brb": "be right back",
"bsod": "blue screen of death",
"btw": "by the way",
"ciao": "Italian for goodbye",
"ctrn": "can't talk right now",
"cul8r": "see you later",
"cya": "see ya",
"dhtb": "don't have the bandwidth",
"f2f": "face to face",
"fubar": "fucked up beyond all recognition",
"fwiw": "for what it's worth",
"fyi": "for your information",
"iam": "in a meeting",
"ianal": "I am not a lawyer",
"ihmb": "I hate my boss",
"iirc": "if I recall correctly",
"imho": "in my humble opinion",
"imo": "in my opinion",
"iow": "in other words",
"irl": "in real life",
"<g>": "grin",
"*g*": "grin",
"gf": "girlfriend",
"gmta": "great minds think alike",
"g2g": "got to go",
"jid": "jabber identifier",
"j/k": "just kidding",
"ok": "okay",
"lol": "laugh out loud",
"l8r": "later",
"msg": "message",
"n/m": "never mind",
"n/p": "no problem",
"oAo": "over and out!",
"omg": "oh my god",
"oob": "out of band",
"otoh": "on the other hand",
"oww": "oops, wrong window!",
"otp": "on the phone",
"pita": "pain in the ass",
"pov": "point of view",
"pw": "password",
"rotfl": "rolling on the floor laughing",
"rsn": "real soon now",
"rtfm": "read the friendly manual",
"slap": "sounds like a plan",
"thx": "thanks",
"tia": "thanks in advance",
"tla": "three-letter arconym",
"ttfn": "ta ta for now",
"ttyl": "talk to you later",
"wb": "welcome back",
"wfm": "works for me",
"wtf": "what the fuck?!",
"wtg": "way to go!",
"xfer": "transfer",
"ymmv": "your mileage may vary",
}
# -*- 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/>.
##
'''
Acronyms expander plugin.
:author: Mateusz Biliński <mateusz@bilinski.it>
:since: 9th June 2008
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
:license: GPL
'''
import sys
import os
from gi.repository import Gtk
from gi.repository import GObject
# Copyright (C) 2008 Mateusz Biliński <mateusz AT bilinski.it>
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Acronyms Expander.
#
# Acronyms Expander 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.
#
# Acronyms Expander 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 Acronyms Expander. If not, see <http://www.gnu.org/licenses/>.
import json
import logging
from pathlib import Path
from functools import partial
from gi.repository import GLib
from gajim.common import app
from gajim.common import configpaths
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import log, log_calls
from gajim.plugins.plugins_i18n import _
class AcronymsExpanderPlugin(GajimPlugin):
from acronyms_expander.acronyms import DEFAULT_DATA
from acronyms_expander.gtk.config import ConfigDialog
log = logging.getLogger('gajim.plugin_system.acronyms')
@log_calls('AcronymsExpanderPlugin')
class AcronymsExpanderPlugin(GajimPlugin):
def init(self):
self.description = _('Replaces acronyms (or other strings) '
'with given expansions/substitutes.')
self.config_dialog = None
'with given expansions/substitutes.')
self.config_dialog = partial(ConfigDialog, self)
self.gui_extension_points = {
'chat_control_base': (self.connect_with_chat_control_base,
self.disconnect_from_chat_control_base)
}
self.config_default_values = {
'INVOKER': (' ', ''),
'ACRONYMS': ({'/slap': '/me slaps',
'PS-': 'plug-in system',
'G-': 'Gajim',
'GNT-': 'https://dev.gajim.org/gajim/gajim/issues',
'GW-': 'https://dev.gajim.org/gajim/gajim/wikis/home',
},
''),
'chat_control_base': (self._connect, self._disconnect)
}
if 'ACRONYMS' not in self.config:
myAcronyms = self.get_own_acronyms_list()
self.config['ACRONYMS'].update(myAcronyms)
@log_calls('AcronymsExpanderPlugin')
def get_own_acronyms_list(self):
data_file = self.local_file_path('acronyms')
if not os.path.isfile(data_file):
return {}
data = open(data_file, 'r', encoding='utf-8')
acronyms = eval(data.read())
data.close()
self._invoker = ' '
self._replace_in_progress = False
self._handler_ids = {}
self.acronyms = self._load_acronyms()
@staticmethod
def _load_acronyms():
try:
data_path = Path(configpaths.get('PLUGINS_DATA'))
except KeyError:
# PLUGINS_DATA was added in 1.0.99.1
return DEFAULT_DATA
path = data_path / 'acronyms' / 'acronyms'
if not path.exists():
return DEFAULT_DATA
with open(path, 'r') as file:
acronyms = json.load(file)
return acronyms
@log_calls('AcronymsExpanderPlugin')
def textbuffer_live_acronym_expander(self, tb):
"""
@param tb gtk.TextBuffer
"""
#assert isinstance(tb,gtk.TextBuffer)
ACRONYMS = self.config['ACRONYMS']
INVOKER = self.config['INVOKER']
t = tb.get_text(tb.get_start_iter(), tb.get_end_iter(), True)
#log.debug('%s %d'%(t, len(t)))
if t and t[-1] == INVOKER:
#log.debug('changing msg text')
base, sep, head=t[:-1].rpartition(INVOKER)
log.debug('%s | %s | %s'%(base, sep, head))
if head in ACRONYMS:
head = ACRONYMS[head]
#log.debug('head: %s'%(head))
t = ''.join((base, sep, head, INVOKER))
#log.debug("setting text: '%s'"%(t))
GObject.idle_add(tb.set_text, t)
@log_calls('AcronymsExpanderPlugin')
def connect_with_chat_control_base(self, chat_control):
d = {}
tv = chat_control.msg_textview
tb = tv.get_buffer()
h_id = tb.connect('changed', self.textbuffer_live_acronym_expander)
d['h_id'] = h_id
chat_control.acronyms_expander_plugin_data = d
return True
@log_calls('AcronymsExpanderPlugin')
def disconnect_from_chat_control_base(self, chat_control):
d = chat_control.acronyms_expander_plugin_data
tv = chat_control.msg_textview
tv.get_buffer().disconnect(d['h_id'])
@staticmethod
def _save_acronyms(acronyms):
try:
data_path = Path(configpaths.get('PLUGINS_DATA'))
except KeyError:
# PLUGINS_DATA was added in 1.0.99.1
return
path = data_path / 'acronyms'
if not path.exists():
path.mkdir(parents=True)
with open(path / 'acronyms', 'w') as file:
json.dump(acronyms, file)
def set_acronyms(self, acronyms):
self.acronyms = acronyms
self._save_acronyms(acronyms)
def _on_buffer_changed(self, _textview, buffer_, contact, account):
if self._replace_in_progress:
return
if buffer_.get_char_count() < 2:
return
# Get iter at cursor
insert_iter = buffer_.get_iter_at_mark(buffer_.get_insert())
if insert_iter.get_offset() < 2:
# We need at least 2 chars and an invoker
return
# Get last char
insert_iter.backward_char()
if insert_iter.get_char() != self._invoker:
log.debug('"%s" not an invoker', insert_iter.get_char())
return
# Get to the start of the last word
word_start_iter = insert_iter.copy()
word_start_iter.backward_word_start()
# Get last word and cut invoker
last_word = word_start_iter.get_slice(insert_iter).strip()
if contact.is_groupchat():
nick_list = app.contacts.get_nick_list(account, contact.jid)
if last_word in nick_list:
log.info('Groupchat participant has same nick as acronym')
return
if contact.is_pm_contact:
if last_word == contact.get_shown_name():
log.info('Contact name equals acronym')
return
substitute = self.acronyms.get(last_word)
if substitute is None:
log.debug('%s not an acronym', last_word)
return
# Replace
word_end_iter = word_start_iter.copy()
word_end_iter.forward_word_end()
GLib.idle_add(self._replace_text,
buffer_,
word_start_iter,
word_end_iter,
substitute)
def _replace_text(self, buffer_, start, end, substitute):
self._replace_in_progress = True
buffer_.delete(start, end)
buffer_.insert(start, substitute)
self._replace_in_progress = False
def _connect(self, chat_control):
textview = chat_control.msg_textview
handler_id = textview.connect('text-changed',
self._on_buffer_changed,
chat_control.contact,
chat_control.account)
self._handler_ids[id(textview)] = handler_id
def _disconnect(self, chat_control):
textview = chat_control.msg_textview
handler_id = self._handler_ids.get(id(textview))
if handler_id is not None:
textview.disconnect(handler_id)
del self._handler_ids[id(textview)]
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Acronyms Expander.
#
# Acronyms Expander 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.
#
# Acronyms Expander 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 Acronyms Expander. If not, see <http://www.gnu.org/licenses/>.
from pathlib import Path
from gi.repository import Gtk
from gi.repository import Gdk
from gajim.common import app
from gajim.plugins.plugins_i18n import _
from gajim.plugins.helpers import get_builder
class ConfigDialog(Gtk.ApplicationWindow):
def __init__(self, plugin, transient):
Gtk.ApplicationWindow.__init__(self)
self.set_application(app.app)
self.set_show_menubar(False)
self.set_title(_('Acronyms Configuration'))
self.set_transient_for(transient)
self.set_default_size(400, 400)
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
self.set_modal(True)
self.set_destroy_with_parent(True)
ui_path = Path(__file__).parent
self._ui = get_builder(ui_path.resolve() / 'config.ui')
self._plugin = plugin
self.add(self._ui.grid)
self._fill_list()
self.show_all()
self._ui.connect_signals(self)
self.connect('destroy', self._on_destroy)
def _fill_list(self):
for acronym, substitute in self._plugin.acronyms.items():
self._ui.acronyms_store.append([acronym, substitute])
def _on_acronym_edited(self, _renderer, path, new_text):
iter_ = self._ui.acronyms_store.get_iter(path)
self._ui.acronyms_store.set_value(iter_, 0, new_text)
def _on_substitute_edited(self, _renderer, path, new_text):
iter_ = self._ui.acronyms_store.get_iter(path)
self._ui.acronyms_store.set_value(iter_, 1, new_text)
def _on_add_clicked(self, _button):
self._ui.acronyms_store.append(['', ''])
row = self._ui.acronyms_store[-1]
self._ui.acronyms_treeview.scroll_to_cell(
row.path, None, False, 0, 0)
self._ui.selection.unselect_all()
self._ui.selection.select_path(row.path)
def _on_remove_clicked(self, _button):
model, paths = self._ui.selection.get_selected_rows()
references = []
for path in paths:
references.append(Gtk.TreeRowReference.new(model, path))
for ref in references:
iter_ = model.get_iter(ref.get_path())
self._ui.acronyms_store.remove(iter_)
def _on_destroy(self, *args):
acronyms = {}
for row in self._ui.acronyms_store:
acronym, substitute = row
if not acronym or not substitute:
continue
acronyms[acronym] = substitute
self._plugin.set_acronyms(acronyms)
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="acronyms_store">
<columns>
<!-- column-name acronym -->
<column type="gchararray"/>
<!-- column-name substitute -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkGrid" id="grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="margin_top">12</property>
<property name="margin_bottom">12</property>
<property name="row_spacing">6</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="acronyms_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="model">acronyms_store</property>
<property name="search_column">1</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="selection">
<property name="mode">multiple</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">True</property>
<property name="title" translatable="yes">Acronym</property>
<property name="clickable">True</property>
<property name="sort_indicator">True</property>
<property name="sort_column_id">0</property>
<child>
<object class="GtkCellRendererText">
<property name="editable">True</property>
<signal name="edited" handler="_on_acronym_edited" swapped="no"/>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">True</property>
<property name="title" translatable="yes">Substitute</property>
<property name="clickable">True</property>
<property name="sort_indicator">True</property>
<property name="sort_column_id">0</property>
<child>
<object class="GtkCellRendererText">
<property name="editable">True</property>
<signal name="edited" handler="_on_substitute_edited" swapped="no"/>
</object>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<signal name="clicked" handler="_on_add_clicked" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-add-symbolic</property>
</object>
</child>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Remove</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">end</property>
<signal name="clicked" handler="_on_remove_clicked" swapped="no"/>
<style>
<class name="destructive-action"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
</interface>
[info]
name: Acronyms Expander
short_name: acronyms_expander
version: 0.3
version: 1.2.4
description: Replaces acronyms (or other strings) with given expansions/substitutes.
authors: Mateusz Biliński <mateusz@bilinski.it>
authors: Philipp Hörist <philipp@hoerist.com>
Mateusz Biliński <mateusz@bilinski.it>
homepage: https://dev.gajim.org/gajim/gajim-plugins/wikis/AcronymsExpanderPlugin
min_gajim_version: 1.1.91
max_gajim_version: 1.2.90
......@@ -26,11 +26,15 @@ Block some incoming messages
from gi.repository import Gtk
import nbxmpp
from gajim.common import app, ged
from gajim.common import app
from gajim.common import ged
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import log, log_calls
from gajim.plugins.gui import GajimPluginConfigDialog
from gajim.plugins.plugins_i18n import _
class AntiSpamPlugin(GajimPlugin):
......@@ -158,13 +162,6 @@ class AntiSpamPlugin(GajimPlugin):
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)
app.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 ' + \
......@@ -176,7 +173,7 @@ class AntiSpamPlugin(GajimPlugin):
else: # for 'normal' type
stanza = nbxmpp.Message(to=jid, body=message, subject='Antispam enabled', typ=obj.mtype)
app.connections[obj.conn.name].connection.send(stanza, now=True)
app.connections[obj.conn.name].connection.send(stanza)
def contain_answer(self, msg, answer):
return answer in msg.split('\n')
......
[info]
name: Anti Spam
short_name: anti_spam
version: 1.4.4
version: 1.4.31
description: Block some incoming messages.
authors = Yann Leboulanger <asterix@lagaule.org>
authors: Yann Leboulanger <asterix@lagaule.org>
Denis Fomin <fominde@gmail.com>
Ilya Kanyukov <ilya.kanukov@gmail.com>
homepage = https://dev.gajim.org/gajim/gajim-plugins/wikis/AntiSpamPlugin
min_gajim_version: 0.16.11
homepage: https://dev.gajim.org/gajim/gajim-plugins/wikis/AntiSpamPlugin
min_gajim_version: 1.1.91
max_gajim_version: 1.2.90
[info]
name: Ayatana Appindicator integration
short_name: appindicator_integration
version: 1.1.0
version: 1.2.0
description: This plugin integrates Gajim with the Ayatana AppIndicator. You must have gir1.2-ayatanaappindicator3-0.1 installed to enable this plugin.
homepage: https://dev.gajim.org/gajim/gajim-plugins/wikis/AppindicatorSupportPlugin
authors: Denis Borenko <borenko@rambler.ru>
Philipp Hörist <philipp@hoerist.com>
min_gajim_version: 1.0.0
min_gajim_version: 1.1.91
max_gajim_version: 1.2.90
......@@ -18,13 +18,19 @@ try:
from gi.repository import AyatanaAppIndicator3 as appindicator
ERRORMSG = None
except (ValueError, ImportError):
ERRORMSG = _('Please install libappindicator3')
ERRORMSG = 'Please install libappindicator3'
from gajim.common import app, ged
from gajim.common import configpaths
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import log_calls
# Since Gajim 1.1.0 _() has to be imported
try:
from gajim.common.i18n import _
except ImportError:
pass
class AppindicatorIntegrationPlugin(GajimPlugin):
......
[info]
name: Banner Tweaks
short_name: banner_tweaks
version: 0.1.3
version: 1.2.0
description: Allows user to tweak chat window banner appearance (eg. make it compact).
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://trac-plugins.gajim.org/wiki/BannerTweaksPlugin
min_gajim_version: 0.16.11
authors: Mateusz Biliński <mateusz@bilinski.it>
homepage: http://trac-plugins.gajim.org/wiki/BannerTweaksPlugin
min_gajim_version: 1.1.91
max_gajim_version: 1.2.90
......@@ -29,8 +29,6 @@ http://trac.gajim.org/attachment/ticket/4133/gajim-chatbanneroptions-svn10008.pa
:license: GPL
'''
import sys
from gi.repository import Gtk
from gi.repository import GObject
from gajim import message_control
......@@ -38,9 +36,16 @@ from gajim.common import app
from gajim.common import helpers
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import log, log_calls
from gajim.plugins.helpers import log_calls
from gajim.plugins.gui import GajimPluginConfigDialog
# Since Gajim 1.1.0 _() has to be imported
try:
from gajim.common.i18n import _
except ImportError:
pass
class BannerTweaksPlugin(GajimPlugin):
@log_calls('BannerTweaksPlugin')
......
[info]
name: Birthday reminder
short_name: birthday_reminder
version: 0.0.5
description: Birthday reminder plugin. Reminder before 5,3,1 and birthday days.
version: 1.2.2
description: Reminds you if a contact of yours has birthday
authors: Evgeniy Popov <evgeniypopov@gmail.com>
homepage: http://trac-plugins.gajim.org/wiki/BirthdayReminderPlugin
min_gajim_version: 0.16.11
Philipp Hörist <philipp@hoerist.com>
homepage: https://dev.gajim.org/gajim/gajim-plugins/wikis/BirthdayReminderPlugin
min_gajim_version: 1.1.91
max_gajim_version: 1.2.90
# This file is part of Birthday Reminder.
#
# Birthday Reminder 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.
#
# Birthday Reminder 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 Birthday Reminder. If not, see <http://www.gnu.org/licenses/>.
import os
import glob
import json
import datetime
from xml.dom.minidom import *
from gi.repository import GObject
import logging
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import log_calls
from gajim.notify import popup
from gi.repository import GLib
from gajim.common import configpaths
from gajim.common import app
from gajim.common import ged
from gajim.plugins import GajimPlugin
from gajim.plugins.plugins_i18n import _
class BirthDayPlugin(GajimPlugin):
log = logging.getLogger('gajim.plugin_system.birthday')
@log_calls('BirthDayPlugin')
def init(self):
TITLE = _('%s has birthday today')
TEXT = _('Send him a message')
class BirthDayPlugin(GajimPlugin):
def init(self):
self.config_dialog = None
self.description = ('Birthday reminder plugin')
self.events_handlers = {
'roster-received': (ged.GUI2, self.roster_received)}
configpath = configpaths.ConfigPaths()
cache_path = configpath.cache_root
self.vcard_path = os.path.join(cache_path, 'vcards') + os.sep
self.timeout_id = 0
self.showed_accounts = []
'vcard-received': (ged.GUI2, self._vcard_received)}
def check_birthdays(self, account=None):
def show_popup(account, jid):
contact_instances = app.contacts.get_contacts(account, jid)
contact = app.contacts.get_highest_prio_contact_from_contacts(
contact_instances)
if contact:
nick = GObject.markup_escape_text(contact.get_shown_name())
try:
image = os.path.dirname(__file__) + os.sep + \
'birthday_reminder_large.png'
except:
image = None
popup('Send message', contact.jid, account, type_='',
path_to_image=image, title=title, text=text + ' ' + nick)
self._timeout_id = None
self._timeout_id_start = None
accounts = app.contacts.get_accounts()
vcards = []
date_dict = {}
for jid in glob.glob(self.vcard_path + '*@*'):
if os.path.isfile(jid):
vcards.append(jid)
self.showed_accounts = []
for xmldoc in vcards:
try:
xml = parse(xmldoc)
except:
pass
else:
name = xml.getElementsByTagName('BDAY')
for node in name:
try:
data = node.childNodes[0].nodeValue
date_dict[xmldoc[len(self.vcard_path):][:-1]] = data
except:
pass
self._birthdays = {}
self._load_birthdays()
today = datetime.date.today()
def activate(self):
self._timeout_id_start = GLib.timeout_add_seconds(
5, self._check_birthdays_at_start)
self._timeout_id = GLib.timeout_add_seconds(
86400, self._check_birthdays)
for key, value in date_dict.items():
def deactivate(self):
if self._timeout_id is not None:
GLib.source_remove(self._timeout_id)
if self._timeout_id_start is not None:
GLib.source_remove(self._timeout_id_start)
def _load_birthdays(self):
path = os.path.join(configpaths.get('MY_DATA'), 'birthdays.json')
if os.path.exists(path):
with open(path, 'r', encoding='utf-8') as file:
content = file.read()
if content:
self._birthdays = json.loads(content)
def _store_birthdays(self):
path = os.path.join(configpaths.get('MY_DATA'), 'birthdays.json')
with open(path, 'w', encoding='utf-8') as file:
json.dump(self._birthdays, file)
def _vcard_received(self, event):
birthday = event.vcard_dict.get('BDAY')
if not birthday:
if event.jid in self._birthdays:
del self._birthdays[event.jid]
log.info('Received empty birthday: %s', event.jid)
else:
try:
convert_date = datetime.datetime.strptime(value, "%Y-%m-%d")
user_bday = datetime.date(today.year, convert_date.month,
convert_date.day)
except:
continue
if user_bday < today:
user_bday = user_bday.replace(year=today.year+1)
time_to_bday = abs(user_bday - today)
title = "BirthDay Reminder"
text = None
if time_to_bday.days > 5:
continue
if time_to_bday.days == 5:
text = "5 days before BDay"
elif time_to_bday.days == 3:
text = "3 days before BDay"
elif time_to_bday.days == 1:
text = "Tomorrow BDay"
elif time_to_bday.days == 0:
text = "Today BDay"
if not text:
continue
if account:
show_popup(account,key)
year, month, day = birthday.split('-')
year = int(year)
month = int(month)
day = int(day)
except Exception:
log.warning('Invalid date: %s', birthday)
if event.jid in self._birthdays:
del self._birthdays[event.jid]
else:
for acct in accounts:
show_popup(account, key)
return True
self._birthdays[event.jid] = (year, month, day)
log.info('Received birthday: %s %s',
event.jid, (year, month, day))
self._store_birthdays()
@log_calls('BirthDayPlugin')
def activate(self):
self.timeout_id = GObject.timeout_add_seconds(24*3600,
self.check_birthdays)
def _check_birthdays_at_start(self):
self._check_birthdays()
@log_calls('BirthDayPlugin')
def deactivate(self):
if self.timeout_id > 0:
GObject.source_remove(self.timeout_id)
def _check_birthdays(self):
log.info('Check birthdays...')
today = datetime.date.today()
for jid, birthdate in self._birthdays.items():
year, month, day = birthdate
if today.month == month and today.day == day:
account, contact = self._find_contact(jid)
if contact is None:
if jid in self._birthdays:
del self._birthdays[jid]
self._store_birthdays()
continue
else:
log.info('Issue notification for %s', jid)
nick = contact.get_shown_name() or jid
app.notification.popup(
'reminder',
jid,
account,
icon_name='trophy-gold',
title=TITLE % GLib.markup_escape_text(nick),
text=TEXT)
return True
@log_calls('BirthDayPlugin')
def roster_received(self, obj):
if obj.conn.name not in self.showed_accounts:
self.check_birthdays(obj.conn.name)
self.showed_accounts.append(obj.conn.name)
@staticmethod
def _find_contact(jid):
accounts = app.contacts.get_accounts()
for account in accounts:
contact = app.contacts.get_contacts(account, jid)
if contact is not None:
return account, contact[0]
from .chatstate import ChatstatePlugin
# -*- coding: utf-8 -*-
##
from gi.repository import GObject
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import log_calls
from gajim.common import ged
from gajim.common import app
from gajim.common import helpers
from gajim 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 '\u200E'
elif bidi == 'AL' or bidi == 'R':
return '\u200F'
return '\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'
'https://dev.gajim.org/gajim/gajim/issues/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 = app.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 = app.interface.roster.model
child_iters = app.interface.roster._get_contact_iter(obj.jid,
obj.conn.name, contact, self.model)
name = GObject.markup_escape_text(contact.get_shown_name())
contact_instances = app.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(name)
name += ' (%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 app.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(app.interface.roster.tree,
False, False)
self.status_color = '#%02x%02x%02x' % (int(color.red * 255),
int(color.green * 255),
int(color.blue * 255))
theme = app.config.get('roster_theme')
self.chatstates = {'active': app.config.get('inmsgcolor'),
'composing': app.config.get_per('themes', theme,
'state_composing_color'),
'inactive': app.config.get_per('themes', theme,
'state_inactive_color'),
'paused': app.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.4
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.
authors = Denis Fomin <fominde@gmail.com>
homepage = https://dev.gajim.org/gajim/gajim-plugins/wikis/ChatstatePlugin
min_gajim_version: 0.16.11
# -*- coding: utf-8 -*-
from gi.repository import Gtk
from gi.repository import Gdk
......@@ -7,6 +5,12 @@ from gajim.common import app
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import log_calls
# Since Gajim 1.1.0 _() has to be imported
try:
from gajim.common.i18n import _
except ImportError:
pass
class ClickableNicknames(GajimPlugin):
......
[info]
name: Clickable Nicknames
short_name: clickable_nicknames
version: 0.6
version: 1.2.0
description: Clickable nicknames in the conversation textview.
authors: Andrey Musikhin <melomansegfault@gmail.com>
Denis Fomin <fominde@gmail.com>
homepage: http://trac-plugins.gajim.org/wiki/ClickableNicknamesPlugin
min_gajim_version: 0.16.11
min_gajim_version: 1.1.91
max_gajim_version: 1.2.90
......@@ -6,7 +6,7 @@ CLIENTS = {
'http://www.asterisk.org/xmpp/client/caps': ['asterisk.png', 'Asterisk'],
'http://ayttm.souceforge.net/caps': ['ayttm.png', 'Ayttm'],
'http://www.barobin.com/caps': ['bayanicq.png', 'Bayanicq'],
'http://bitlbee.org/xmpp/caps': ['bitlbee.png', 'Bitlbee'],
'http://bitlbee.org/xmpp/caps': ['bitlbee.png', 'BitlBee'],
'http://simpleapps.ru/caps#blacksmith': ['bot.png', 'Blacksmith'],
'http://blacksmith-2.googlecode.com/svn/': ['bot.png', 'Blacksmith-2'],
'http://jabber.pdg.pl/caps': ['bombus-klub.png', 'Bombus-klub'],
......@@ -26,8 +26,10 @@ CLIENTS = {
['coccinella.png', 'Coccinella'],
'http://conversations.im': ['conversations.png', 'Conversations'],
'Conversations Legacy': ['conversations-legacy.png', 'Conversations Legacy'],
'https://conversejs.org': ['conversejs.png', 'Converse'],
'http://digsby.com/caps': ['digsby.png', 'Digsby'],
'https://dino.im': ['dino.png', 'Dino'],
'http://emacs-jabber.sourceforge.net': ['emacs.png', 'Emacs Jabber client'],
'http://emess.eqx.su/caps': ['emess.png', 'Emess'],
'http://live.gnome.org/empathy/caps': \
['telepathy.freedesktop.org.png', 'Empathy'],
......@@ -100,7 +102,10 @@ CLIENTS = {
'http://pidgin.im/caps': ['pidgin.png', 'Pidgin'],
'http://pigeon.vpro.ru/caps': ['pigeon.png', 'Pigeon'],
'Pix-Art Messenger': ['pixart.png', 'Pix-Art Messenger'],
'http://jabber.pix-art.de': ['pixart.png', 'Pix-Art Messenger'],
'httр://sleekxmpp.com/ver/1.1.11': ['poezio.png', 'Poezio'],
'https://poez.io': ['poezio.png', 'Poezio'],
'http://www.profanity.im': ['profanity.png', 'Profanity'],
'http://psi-im.org/caps': ['psi.png', 'Psi'],
'http://psi-plus.com': ['psiplus.png', 'Psi+'],
'http://psi-dev.googlecode.com/caps': ['psiplus.png', 'Psi+'],
......@@ -144,8 +149,10 @@ CLIENTS = {
'http://witcher-team.ucoz.ru/': ['bot.png', 'Witcher'],
'http://online.yandex.ru/caps': ['yaonline.png', 'Yaonline'],
'http://www.igniterealtime.org/projects/smack/': ['xabber.png', 'Xabber'],
'https://www.xabber.com/': ['xabber.png', 'Xabber'],
'http://www.xfire.com/': ['xfire.png', 'Xfire'],
'http://www.xfire.com/caps': ['xfire.png', 'Xfire'],
'https://www.yaxim.org/': ['yaxim.png', 'Yaxim'],
'http://xu-6.jabbrik.ru/caps': ['bot.png', 'XU-6'],
}
LIBPURPLE_CLIENTS = {
......
# -*- coding: utf-8 -*-
##
import os