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 (1037)
Showing
with 845 additions and 287 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__/
\ No newline at end of file
__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
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]
### Branches
# Contributing
- master (Python3/Gtk3 for Gajim master branch
- gajim_0.16 (Python2/Gtk2 for Gajim 0.16.x)
## Branches
### Git
- master
- gajim_1.2
- gajim_1.1
- gajim_1.0
- gajim_0.16
If you are not familiar with Git please read the [HowTo](https://dev.gajim.org/gajim/gajim/wikis/howtogit)
## 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 necessary
in the extended commit message
- 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
## 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.
# 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
## 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 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. 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)|
## Share / Improve Plugins
*Note: Using master branch for plugins requires frequent updates of both Gajim and plugins!*
You have written a new plugin or want to improve an already existing one?
## Development
First, Thanks for that! Here is how to do that:
You have written a new plugin or want to improve an existing one?
- 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)
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)
- When you are finished make a pull request against the main repository
- 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**
**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**
**Please do not 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).
from .acronyms_expander import AcronymsExpanderPlugin
from .acronyms_expander import AcronymsExpanderPlugin # pyright: ignore # noqa: F401
{"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",
"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",
}
# -*- 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
# 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.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.p.acronyms")
@log_calls('AcronymsExpanderPlugin')
def init(self):
self.description = _('Replaces acronyms (or other strings) '
'with given expansions/substitutes.')
self.config_dialog = None
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 = {
'chat_control_base': (self.connect_with_chat_control_base,
self.disconnect_from_chat_control_base)
"message_input": (self._connect, None),
"switch_contact": (self._on_switch_contact, None),
}
self._invoker = " "
self._replace_in_progress = False
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',
},
''),
}
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._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
@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: 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>
[info]
name: Acronyms Expander
short_name: acronyms_expander
version: 0.3
description: Replaces acronyms (or other strings) with given expansions/substitutes.
authors: Mateusz Biliński <mateusz@bilinski.it>
homepage: https://dev.gajim.org/gajim/gajim-plugins/wikis/AcronymsExpanderPlugin
<?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
## [Unreleased]
### Added
- Sending confirmation message that anti spam check was passed. It can be disabled in plugin gui config.
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
### Changed
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
### Fixed
- Messages that was sent before correct answer was marked as received
- Chat between the two antispam plugins
## [0.4.2] - 2016-11-28
### Added
- Anti spam question functionality
- This CHANGELOG
- README with some explanation of functionality
### Changed
- homepage in manifest.ini
0.4.2 / 2016-11-28
- Added anti spam question functionality
- Added README with some explanation of functionality
- Added website in manifest.ini
# Anti_spam Plugin for Gajim
This Plugin allows you to dissociate itself from the spam.
## Installation
Use special plugin, that manages automatic download and installation of others plugins, it is called Plugin Installer.
## Options
### Block pubsub
Block incoming messages from pubsub
### Message size limit
Block incoming messages that have size more than configured. Default value -1 mean that any sized messages are coming.
### Anti spam question
Block incoming messages from users not in your roster. In response, the Plugin sends a question that you configured. After correct answer(also configurable) you will receive all new messages from user.
**Attention!** All messages before correct answer will be lost.
Also you can enable this function, in Plugin config, for conference private messages. In some servers, the question in conference private does not reach your interlocutor. This can lead to the fact that you will not receive any messages from him, and he will not know it.
\ No newline at end of file
from .anti_spam import AntiSpamPlugin
from .anti_spam import AntiSpamPlugin # pyright: ignore # noqa: F401