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
Commits on Source (1544)
Showing
with 938 additions and 131 deletions
from typing import Any
import functools
import json
import os
import sys
from collections.abc import Iterator
from ftplib import FTP_TLS
from pathlib import Path
from shutil import make_archive
import requests
from rich.console import Console
PackageT = tuple[dict[str, Any], Path]
ManifestT = dict[str, Any]
PackageIndexT = dict[str, Any]
FTP_URL = "panoramix.gajim.org"
FTP_USER = os.environ["FTP_USER"]
FTP_PASS = os.environ["FTP_PASS"]
REPOSITORY_FOLDER = "plugins/master"
PACKAGE_INDEX_URL = "https://ftp.gajim.org/plugins/master/package_index.json"
REPO_ROOT = Path(__file__).parent.parent
BUILD_PATH = REPO_ROOT / "build"
REQUIRED_KEYS = {
"authors",
"description",
"homepage",
"name",
"platforms",
"requirements",
"short_name",
"version",
}
console = Console()
def ftp_connection(func: Any) -> Any:
@functools.wraps(func)
def func_wrapper(*args: Any) -> None:
ftp = FTP_TLS(FTP_URL, FTP_USER, FTP_PASS) # noqa: S321
console.print("Successfully connected to", FTP_URL)
func(ftp, *args)
ftp.quit()
console.print("Quit")
return func_wrapper
def is_manifest_valid(manifest: ManifestT) -> bool:
manifest_keys = set(manifest.keys())
return REQUIRED_KEYS.issubset(manifest_keys)
def download_package_index() -> ManifestT:
console.print("Download package index")
r = requests.get(PACKAGE_INDEX_URL, timeout=30)
if r.status_code == 404:
return {}
r.raise_for_status()
index = r.json()
return index
def iter_manifests() -> Iterator[PackageT]:
for path in REPO_ROOT.rglob("plugin-manifest.json"):
with path.open() as f:
manifest = json.load(f)
yield manifest, path.parent
def find_plugins_to_publish(index: PackageIndexT) -> list[PackageT]:
packages_to_publish: list[PackageT] = []
for manifest, path in iter_manifests():
if not is_manifest_valid(manifest):
sys.exit("Invalid manifest found")
short_name = manifest["short_name"]
version = manifest["version"]
try:
index["plugins"][short_name][version]
except KeyError:
packages_to_publish.append((manifest, path))
console.print("Found package to publish:", path.stem)
return packages_to_publish
def get_release_zip_name(manifest: ManifestT) -> str:
short_name = manifest["short_name"]
version = manifest["version"]
return f"{short_name}_{version}"
def get_dir_list(ftp: FTP_TLS) -> set[str]:
return {x[0] for x in ftp.mlsd()}
def upload_file(ftp: FTP_TLS, filepath: Path) -> None:
name = filepath.name
console.print("Upload file", name)
with open(filepath, "rb") as f:
ftp.storbinary("STOR " + name, f)
def create_release_folder(ftp: FTP_TLS, packages_to_publish: list[PackageT]) -> None:
folders = {manifest["short_name"] for manifest, _ in packages_to_publish}
dir_list = get_dir_list(ftp)
missing_folders = folders - dir_list
for folder in missing_folders:
ftp.mkd(folder)
@ftp_connection
def deploy(ftp: FTP_TLS, packages_to_publish: list[PackageT]) -> None:
ftp.cwd(REPOSITORY_FOLDER)
create_release_folder(ftp, packages_to_publish)
for manifest, path in packages_to_publish:
package_name = manifest["short_name"]
zip_name = get_release_zip_name(manifest)
zip_path = BUILD_PATH / f"{zip_name}.zip"
image_path = path / f"{package_name}.png"
make_archive(str(BUILD_PATH / zip_name), "zip", path)
ftp.cwd(package_name)
upload_file(ftp, zip_path)
if image_path.exists():
upload_file(ftp, image_path)
ftp.cwd("..")
console.print("Deployed", package_name)
if __name__ == "__main__":
index = download_package_index()
packages_to_publish = find_plugins_to_publish(index)
if not packages_to_publish:
console.print("No new packages deployed")
else:
deploy(packages_to_publish)
syntax: glob
*.pyc
*.pyo
__pycache__/
*.*~
*~
*#
.vscode
.mypy_cache
*.swp
*.sublime-workspace
*.sublime-project
build/
.venv
image: plugins-master:latest
stages:
- test
- deploy
deploy-plugins:
stage: deploy
script:
- python3 .ci/deploy.py
test-isort:
image: gajim-test
stage: test
rules:
- changes:
- "**/*.py"
script:
- isort --version
- isort --check .
interruptible: true
test-ruff:
image: gajim-test
stage: test
rules:
- changes:
- "**/*.py"
script:
- ruff --version
- ruff check .
interruptible: true
test-black:
image: gajim-test
stage: test
rules:
- changes:
- "**/*.py"
script:
- pip install black==24.10.0
- black --version
- black . --check
interruptible: true
**Please first check if another issue has been opened for your problem**
## Versions
- OS:
- Gajim version:
- Plugin version:
- GTK version:
- Python-nbxmpp version:
## Steps to reproduce the problem
1.
1.
1.
## Expected behavior
## Actual behavior
**Please note by far the quickest way to get a new feature is to file a Merge Request.**
## Description of the new feature
# use glob syntax.
syntax: glob
*.pyc
*.pyo
*.db
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.9.7
hooks:
- id: ruff
exclude: ".githooks/"
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
- id: codespell
pass_filenames: false
additional_dependencies:
- tomli
- repo: https://github.com/RobertCraigie/pyright-python
rev: v1.1.394
hooks:
- id: pyright
pass_filenames: false
additional_dependencies:
- nbxmpp @ git+https://dev.gajim.org/gajim/python-nbxmpp.git
- PyGObject-stubs @ git+https://github.com/pygobject/pygobject-stubs.git
stages: [manual]
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
- repo: https://github.com/psf/black
# The `refs/tags/<tag>:refs/tags/<tag>` is needed for black's required-version to work:
# https://github.com/psf/black/issues/2493#issuecomment-1081987650
rev: 'refs/tags/24.10.0:refs/tags/24.10.0'
hooks:
- id: black
- repo: https://github.com/fsfe/reuse-tool
rev: v5.0.2
hooks:
- id: reuse
stages: [manual]
# Contributing
## Branches
- master
- gajim_1.2
- gajim_1.1
- gajim_1.0
- gajim_0.16
## Git
If you are not familiar with Git, please read the [HowTo](https://dev.gajim.org/gajim/gajim/wikis/howtogit)
### Git Commit Messages
- Start your commit message with the Plugin name, example: [omemo] Added ..
- Use the present tense ("Add feature" not "Added feature")
- Limit the first line to 50 characters or less, add whatever you feel is necessary in the extended commit message
# Gajim Plugins
In this place you will find all plugins that are written for [Gajim](https://gajim.org) by the community. If you experience any problems with those plugins, please report them here.
## How to install plugins
**Note:** Some plugins have external dependencies that need to be installed separately.
Check the [plugin's wiki page](https://dev.gajim.org/gajim/gajim-plugins/-/wikis/home#plugins-list) for details.
There are several ways to install a plugin:
- 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/`
**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. Gajim >=1.4 uses the `master` branch.**
| Version | Plugins branch |
| ------- | -------------- |
|Gajim master|[master branch](https://dev.gajim.org/gajim/gajim-plugins/tree/master)|
|Gajim 1.3|[1.3 branch](https://dev.gajim.org/gajim/gajim-plugins/tree/gajim_1.3)|
|Gajim 1.2|[1.2 branch](https://dev.gajim.org/gajim/gajim-plugins/tree/gajim_1.2)|
|Gajim 1.1|[1.1 branch](https://dev.gajim.org/gajim/gajim-plugins/tree/gajim_1.1)|
|Gajim 1.0|[1.0 branch](https://dev.gajim.org/gajim/gajim-plugins/tree/gajim_1.0)|
*Note: Using master branch for plugins requires frequent updates of both Gajim and plugins!*
## Development
You have written a new plugin or want to improve an existing one?
First, thanks for that! Here is how to start:
- 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)
- Check `./scripts/dev_env.sh` to get a environment with dependencies installed
- When you are finished, do a merge request against the main plugins 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](xmpp:gajim@conference.gajim.org?join)**
**Please do not use dev.gajim.org for any projects that are not directly for the benefit of Gajim!**
## Plugins list
All available plugins are listed [here](https://dev.gajim.org/gajim/gajim-plugins/wikis/home).
from .acronyms_expander import AcronymsExpanderPlugin # pyright: ignore # noqa: F401
DEFAULT_DATA = {
"afaik": "as far as I know",
"afair": "as far as I can remember",
"afaict": "as far as I can tell",
"afk": "away from keyboard",
"asap": "as soon as possible",
"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",
"dm": "direct message",
"f2f": "face to face",
"fubar": "fucked up beyond all recognition",
"fwiw": "for what it's worth",
"fyi": "for your information",
"<g>": "grin",
"*g*": "grin",
"gf": "girlfriend",
"gmta": "great minds think alike",
"g2g": "got to go",
"iam": "in a meeting",
"ianal": "I am not a lawyer",
"idk": "I don't know",
"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",
"jid": "jabber identifier",
"j/k": "just kidding",
"lmao": "laughing my ass off",
"ok": "okay",
"lol": "laugh out loud",
"l8r": "later",
"msg": "message",
"n/m": "nevermind",
"n/p": "no problem",
"nvm": "nevermind",
"oAo": "over and out!",
"omg": "oh my god",
"oob": "out of band",
"op": "original poster",
"ot": "off topic",
"otoh": "on the other hand",
"otp": "on the phone",
"oww": "oops, wrong window!",
"pita": "pain in the ass",
"pm": "private message",
"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",
"smh": "shaking my head",
"tbh": "to be honest",
"thx": "thanks",
"tia": "thanks in advance",
"til": "today I learned",
"tldr": "too long, didn't read",
"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?!",
"wth": "what the hell",
"wtg": "way to go!",
"xfer": "transfer",
"ymmv": "your mileage may vary",
}
acronyms_expander/acronyms_expander.png

189 B

# 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/>.
from __future__ import annotations
import json
import logging
from functools import partial
from pathlib import Path
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gtk
from gajim.common import configpaths
from gajim.common import types
from gajim.common.modules.contacts import GroupchatContact
from gajim.gtk.message_input import MessageInputTextView
from gajim.plugins import GajimPlugin
from gajim.plugins.plugins_i18n import _
from acronyms_expander.acronyms import DEFAULT_DATA
from acronyms_expander.gtk.config import ConfigDialog
log = logging.getLogger("gajim.p.acronyms")
class AcronymsExpanderPlugin(GajimPlugin):
def init(self) -> None:
self.description = _(
"Replaces acronyms (or other strings) with given expansions/substitutes."
)
self.config_dialog = partial(ConfigDialog, self)
self.gui_extension_points = {
"message_input": (self._connect, None),
"switch_contact": (self._on_switch_contact, None),
}
self._invoker = " "
self._replace_in_progress = False
self._signal_id = None
self._message_input = None
self._contact = None
self.acronyms = self._load_acronyms()
@staticmethod
def _load_acronyms() -> dict[str, str]:
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 path.open("r") as file:
acronyms = json.load(file)
return acronyms
@staticmethod
def _save_acronyms(acronyms: dict[str, str]) -> None:
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)
filepath = path / "acronyms"
with filepath.open("w") as file:
json.dump(acronyms, file)
def set_acronyms(self, acronyms: dict[str, str]) -> None:
self.acronyms = acronyms
self._save_acronyms(acronyms)
def _on_buffer_changed(self, message_input: MessageInputTextView) -> None:
if self._contact is None:
# If no chat has been activated yet
return
if self._replace_in_progress:
return
buffer_ = message_input.get_buffer()
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()
result = insert_iter.backward_search(
self._invoker, Gtk.TextSearchFlags.VISIBLE_ONLY, None
)
if result is None:
word_start_iter = buffer_.get_start_iter()
else:
_, word_start_iter = result
# Get last word and cut invoker
last_word = word_start_iter.get_slice(insert_iter)
if isinstance(self._contact, GroupchatContact):
if last_word in self._contact.get_user_nicknames():
log.info("Groupchat participant has same nick as acronym")
return
if self._contact.is_pm_contact:
if last_word == self._contact.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
GLib.idle_add(
self._replace_text, buffer_, word_start_iter, insert_iter, substitute
)
def _replace_text(
self,
buffer_: Gtk.TextBuffer,
start: Gtk.TextIter,
end: Gtk.TextIter,
substitute: str,
) -> None:
self._replace_in_progress = True
buffer_.delete(start, end)
buffer_.insert(start, substitute)
self._replace_in_progress = False
def _on_switch_contact(self, contact: types.ChatContactT) -> None:
self._contact = contact
def _connect(self, message_input: MessageInputTextView) -> None:
self._message_input = message_input
self._signal_id = message_input.connect(
"buffer-changed", self._on_buffer_changed
)
def deactivate(self) -> None:
assert self._message_input is not None
assert self._signal_id is not None
if GObject.signal_handler_is_connected(self._message_input, self._signal_id):
self._message_input.disconnect(self._signal_id)
# 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 __future__ import annotations
from typing import cast
from typing import TYPE_CHECKING
from pathlib import Path
from gi.repository import Gtk
from gajim.gtk.widgets import GajimAppWindow
from gajim.plugins.helpers import get_builder
from gajim.plugins.plugins_i18n import _
if TYPE_CHECKING:
from ..acronyms_expander import AcronymsExpanderPlugin
class ConfigBuilder(Gtk.Builder):
acronyms_store: Gtk.ListStore
box: Gtk.Box
acronyms_treeview: Gtk.TreeView
selection: Gtk.TreeSelection
acronym_renderer: Gtk.CellRendererText
sub_renderer: Gtk.CellRendererText
add_button: Gtk.Button
remove_button: Gtk.Button
class ConfigDialog(GajimAppWindow):
def __init__(self, plugin: AcronymsExpanderPlugin, transient: Gtk.Window) -> None:
GajimAppWindow.__init__(
self,
name="AcronymsConfigDialog",
title=_("Acronyms Configuration"),
default_width=400,
default_height=400,
transient_for=transient,
modal=True,
)
ui_path = Path(__file__).parent
self._ui = cast(
ConfigBuilder, get_builder(str(ui_path.resolve() / "config.ui"))
)
self._plugin = plugin
self.set_child(self._ui.box)
self._fill_list()
self._connect(self._ui.acronym_renderer, "edited", self._on_acronym_edited)
self._connect(self._ui.sub_renderer, "edited", self._on_substitute_edited)
self._connect(self._ui.add_button, "clicked", self._on_add_clicked)
self._connect(self._ui.remove_button, "clicked", self._on_remove_clicked)
self._connect(self.window, "close-request", self._on_close_request)
self.show()
def _cleanup(self) -> None:
del self._plugin
def _fill_list(self) -> None:
for acronym, substitute in self._plugin.acronyms.items():
self._ui.acronyms_store.append([acronym, substitute])
def _on_acronym_edited(
self, _renderer: Gtk.CellRendererText, path: str, new_text: str
) -> None:
iter_ = self._ui.acronyms_store.get_iter(path)
self._ui.acronyms_store.set_value(iter_, 0, new_text)
def _on_substitute_edited(
self, _renderer: Gtk.CellRendererText, path: str, new_text: str
) -> None:
iter_ = self._ui.acronyms_store.get_iter(path)
self._ui.acronyms_store.set_value(iter_, 1, new_text)
def _on_add_clicked(self, _button: Gtk.Button) -> None:
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: Gtk.Button) -> None:
res = self._ui.selection.get_selected_rows()
if res is None:
return
model, paths = res
references: list[Gtk.TreeRowReference] = []
for path in paths:
ref = Gtk.TreeRowReference.new(model, path)
assert ref is not None
references.append(ref)
for ref in references:
path = ref.get_path()
assert path is not None
iter_ = model.get_iter(path)
self._ui.acronyms_store.remove(iter_)
def _on_close_request(self, win: Gtk.ApplicationWindow) -> None:
acronyms: dict[str, str] = {}
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"?>
<interface>
<requires lib="gtk" version="4.0"/>
<object class="GtkListStore" id="acronyms_store">
<columns>
<column type="gchararray"/>
<column type="gchararray"/>
</columns>
</object>
<object class="GtkBox" id="box">
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow">
<property name="focusable">1</property>
<property name="vexpand">1</property>
<property name="hexpand">1</property>
<property name="child">
<object class="GtkTreeView" id="acronyms_treeview">
<property name="focusable">1</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">1</property>
<property name="title" translatable="yes">Acronym</property>
<property name="clickable">1</property>
<property name="sort_indicator">1</property>
<property name="sort_column_id">0</property>
<child>
<object class="GtkCellRendererText" id="acronym_renderer">
<property name="editable">1</property>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">1</property>
<property name="title" translatable="yes">Substitute</property>
<property name="clickable">1</property>
<property name="sort_indicator">1</property>
<property name="sort_column_id">0</property>
<child>
<object class="GtkCellRendererText" id="sub_renderer">
<property name="editable">1</property>
</object>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="css-classes">toolbar</property>
<style>
<class name="inline-toolbar"/>
</style>
<child>
<object class="GtkButton" id="add_button">
<property name="tooltip_text" translatable="yes">Add</property>
<property name="icon_name">list-add-symbolic</property>
</object>
</child>
<child>
<object class="GtkButton" id="remove_button">
<property name="tooltip_text" translatable="yes">Remove</property>
<property name="icon_name">list-remove-symbolic</property>
</object>
</child>
</object>
</child>
</object>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<component type="addon">
<id>org.gajim.Gajim.Plugin.acronyms_expander</id>
<extends>org.gajim.Gajim</extends>
<name>Acronyms Expander Plugin</name>
<summary>Replace acronyms (or other strings) with given expansions/substitutes</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": [
"Philipp Hörist <philipp@hoerist.com>",
"Mateusz Biliński <mateusz@bilinski.it>"
],
"description": "Replaces acronyms (or other strings) with given expansions/substitutes.",
"homepage": "https://dev.gajim.org/gajim/gajim-plugins/wikis/AcronymsExpanderPlugin",
"config_dialog": true,
"name": "Acronyms Expander",
"platforms": [
"others",
"linux",
"darwin",
"win32"
],
"requirements": [
"gajim>=2.0.0"
],
"short_name": "acronyms_expander",
"version": "1.6.0"
}
\ No newline at end of file
1.5.1 / 2020-05-03
- Rework plugin
- Adapt to upstream changes in Gajim and python-nbxmpp
- Use new configuration dialog
- Remove pubsub blocking (Gajim handles these messages differently now)
- Remove domain blocking feature (Gajim handles this)
1.4.3 / 2016-12-04
- Added filtering 'normal' type messages
- User from private conference conversation permanently stored in file
- Switched to GTK3
- Messages sent before the correct answer were marked as received
- Fixed chat between the two antispam plugins
0.4.2 / 2016-11-28
- Added anti spam question functionality
- Added README with some explanation of functionality
- Added website in manifest.ini
from anti_spam import AntiSpamPlugin
from .anti_spam import AntiSpamPlugin # pyright: ignore # noqa: F401
# -*- 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/>.
##
'''
Block some incoming messages
:author: Yann Leboulanger <asterix@lagaule.org>
:since: 16 August 2012
:copyright: Copyright (2012) Yann Leboulanger <asterix@lagaule.org>
:license: GPLv3
'''
import gtk
from common import ged
from plugins import GajimPlugin
from plugins.helpers import log, log_calls
from plugins.gui import GajimPluginConfigDialog
class AntiSpamPlugin(GajimPlugin):
@log_calls('AntiSpamPlugin')
def init(self):
self.description = _('Allows to block some kind of incoming messages')
self.config_dialog = AntiSpamPluginConfigDialog(self)
self.gui_extension_points = {
}
self.events_handlers = {
'atom-entry-received': (ged.POSTCORE,
self._nec_atom_entry_received),
'message-received': (ged.PRECORE,
self._nec_decrypted_message_received_received),
'subscribe-presence-received': (ged.POSTCORE,
self._nec_subscribe_presence_received),
}
self.config_default_values = {
'block_pubsub_messages': (False, 'If True, Gajim will block incoming messages from pubsub.'),
'disable_xhtml_muc': (False, ''),
'disable_xhtml_pm': (False, ''),
'block_subscription_requests': (False, ''),
}
@log_calls('AntiSpamPlugin')
def _nec_atom_entry_received(self, obj):
if self.config['block_pubsub_messages']:
log.info('discarding pubdubd message')
return True
@log_calls('AntiSpamPlugin')
def _nec_decrypted_message_received_received(self, obj):
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 \
obj.resource and obj.mtype == 'chat':
self.remove_xhtml(obj)
return False
@log_calls('AntiSpamPlugin')
def _nec_subscribe_presence_received(self, obj):
if self.config['block_subscription_requests'] and \
not gajim.contacts.get_contacts(obj.conn.name, obj.jid):
log.info('discarding subscription request from %s' % obj.jid)
return True
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.child.pack_start(self.config_vbox)
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'])
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()
# 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/>.
"""
:author: Yann Leboulanger <asterix@lagaule.org>
:since: 16 August 2012
:copyright: Copyright (2012) Yann Leboulanger <asterix@lagaule.org>
:license: GPLv3
"""
from functools import partial
from gajim.plugins import GajimPlugin
from gajim.plugins.plugins_i18n import _
from anti_spam.config_dialog import AntiSpamConfigDialog
from anti_spam.modules import anti_spam
class AntiSpamPlugin(GajimPlugin):
def init(self) -> None:
self.description = _(
"Allows you to block various kinds of incoming "
"messages (Spam, XHTML formatting, etc.)"
)
self.config_dialog = partial(AntiSpamConfigDialog, self)
self.config_default_values = {
"disable_xhtml_muc": (False, ""),
"disable_xhtml_pm": (False, ""),
"block_subscription_requests": (False, ""),
"msgtxt_limit": (0, ""),
"msgtxt_question": ("12 x 12 = ?", ""),
"msgtxt_answer": ("", ""),
"antispam_for_conference": (False, ""),
"block_domains": ("", ""),
"whitelist": ([], ""),
}
self.gui_extension_points = {}
self.modules = [anti_spam]