Skip to content
Snippets Groups Projects

New plugin: Stickers

Open Alexander requested to merge PapaTutuWawa/gajim-plugins:feat/stickers-plugin into master
7 unresolved threads

This plugin implements XEP-0449 and allows the user to send and receive stickers. Sticker packs that are not published on the user's account can be uploaded and published on sign-in, if configured. The same holds true for stickers that are published on the user's account but not locally present, except that they are downloaded on sign-in. Animated stickers, though currently only GIFs, are also supported with the ability to turn off animations.

stickers_chat

The button that shows a menu for selecting the sticker to send is also searchable and updates when a new sticker pack gets added. It searches for matchin in either a sticker's "description" (desc) or one of its "suggests".

stickers_popup

When a sticker is not known, the sticker pack gets automatically downloaded and added. This, however, can be turned off. In that case, a button is shown that asks the user if the sticker pack should be downloaded.

stickers_manual

As just deleting the folder containing the sticker pack does not delete it from the account, this plugin also features a UI for removing sticker packs:

stickers_list

There are, however, currently two problems:

  • The stickers are always sent unencrypted. At least with OMEMO, all non-whitelisted tags are filtered out before sending.
  • As a rework of the conversation view is currently in progress, I would have to rewrite the plugin a bit when that MR is merged.

I should note that the plugin does not support importing or creating sticker packs. For my testing purposes I have created a script that takes a JSON representation of a sticker pack - that this plugin uses internally - and updates it so that the plugin can work with it. I feel like it is out of scope of the plugin, or at least of this first MR.

Under Linux the plugin requires stickers to be located at ~/.local/share/gajim/plugins_data/stickers/<sticker pack ID>/<sticker file>. The folder must contain a info.json file that describes the sticker pack and looks like this:

{
  "id": "0c58051a78fcd4ecf982f136",
  "name": "A sticker pack",
  "summary": "A description",
  "stickers": [
    {
      "type": "image/png",
      "hashes": [
        {
          "algo": "sha-256",
          "value": "8b76e01a4cd9ef8b3d63541459b1718b62c6073271b4e14cd0a9d3155964f13b"
        }
      ],
      "size": "14015",
      "dimension": "113x110",
      "desc": ":grin:",
      "suggests": [":blobgrin:"],
      "filename": "grin.png",
      "url": ""
    },
    [...]
  ]
}

The URL attribute of a sticker can be left empty as the plugin updates it automatically when the sticker pack has been published.

I tested this plugin on two Linux machines and it works. It requires a Gajim version newer than a290db02.

Merge request reports

Pipeline #7756 passed

Pipeline passed for e89bafc5 on PapaTutuWawa:feat/stickers-plugin

Approval is optional
Merge blocked: 2 checks failed
Unresolved discussions must be resolved.
Merge request must be rebased, because a fast-forward merge is not possible.

Merge details

  • The source branch is 357 commits behind the target branch.
  • 1 commit will be added to master.
  • Source branch will be deleted.

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
  • Nice work! I’m really looking forward for this :)

    Since gajim!733 (closed) won’t be part of Gajim 1.3, this is nothing to worry about at this point.

  • @PapaTutuWawa would you be so kind and provide an example sticker pack?

    Edited by Daniel Brötzmann
  • Alexander added 3 commits

    added 3 commits

    • af0e79e7 - [stickers] Fix new sticker packs not being uploaded
    • c68bc2f8 - [stickers] Fix description
    • 5ff2ddd9 - [stickers] Fix translations

    Compare with previous version

  • Alexander added 1 commit

    added 1 commit

    • 69679f12 - [stickers] Disallow sending stickers when using encryption

    Compare with previous version

  • Alexander added 5 commits

    added 5 commits

    • e3b89945 - [stickers] Prepend a newline to stickers
    • 1d8d5fbe - [stickers] Lowercase GTK widget IDs and fix translation mistake
    • fb3dbf48 - [stickers] Add a reload button
    • 344bd6e3 - [stickers] Remove namespace declarations
    • 614edb2b - [stickers] Fix search not resetting

    Compare with previous version

  • Alexander added 1 commit

    added 1 commit

    • a43c6f87 - [stickers] Fix more get_object uses

    Compare with previous version

  • Alexander added 5 commits

    added 5 commits

    • b3e66ee0 - [stickers] Various UI improvements
    • b6dd6219 - [stickers] Refactor StickersButton to work with models
    • 2d88f372 - [stickers] Ask if the user really wants to reload all sticker packs
    • fea91a10 - [stickers] Some small code improvements
    • a2ece3b0 - [stickers] To ease my mind, check for another path escape on download

    Compare with previous version

  • So I now did the following things:

    • I moved the entire UI where stickers are shown to using models. Maybe I went a bit ham, but I implemented a proxy model for filtering, as I was not sure whether I could just depend on libdazzle for such a model.
    • I added a button to show the folder containing all sticker packs.
    • Not uploaded stickers now show a button to upload and publish them. Additionally, if a user tries to send a sticker from a non-uploaded sticker, a prompt will show up asking if the sticker pack should be uploaded.

    new_settings

  • Nice, that looks useful! I'd use some 'upload' icon rather than add/plus.

  • Yeah, I agree. It is just the first icon I found while scrolling through the icon list in Glade that somewhat implied the action. All other icons, including promising ones like document-send or document-send-symbolic (Based on the description from the opendesktop spec: [...] Should be an arrow pointing up and away from a hard disk.) don't make me feel like I am uploading a file. This, however, may also be an issue with my setup as I am on KDE.

    • fails for me on first start

      Traceback (most recent call last):
        File "/home/lovetox/projects/gajim/gajim/plugins/pluginmanager.py", line 290, in add_plugin
          plugin_obj = plugin_class()
        File "/home/lovetox/projects/gajim/gajim/plugins/gajimplugin.py", line 177, in __init__
          self.init()
        File "/home/lovetox/.local/share/gajim/plugins/stickers/plugin.py", line 204, in init
          self.config_dialog = StickersConfigDialog(self)
        File "/home/lovetox/projects/gajim/gajim/plugins/gui.py", line 324, in __init__
          self.init()
        File "/home/lovetox/.local/share/gajim/plugins/stickers/gtk/config.py", line 50, in init
          getattr(self._ui, setting).set_active(self.plugin.config[setting.upper()])
        File "/home/lovetox/projects/gajim/gajim/plugins/gajimplugin.py", line 219, in __getitem__
          self.data[key] = self.plugin.config_default_values[key][0]
      KeyError: 'DOWNLOAD_NEW'
      
    • That should be fixed now.

    • Please register or sign in to reply
    • Are you aware that the module in the modules folder get instantiated for every account in Gajim? Did you think about what happens when in Gajim more than one account is active?

      Normally you would want account specific data to be handled and stored in the sticker_module.py. Right now you send events to the plugin.py and store it there. This makes it necessary that you have multiple dict stores with the account as key and need to handle stuff like new accounts getting added and other accounts vanish etc. Did you have a specific reason why to store the account specific packs there?

    • Are you aware that the module in the modules folder get instantiated for every account in Gajim?

      I would say kinda yes, kinda no. This code is mainly based on an earlier prototype before the XEP even existed. There I did much less of this state tracking than now.

      However, I would argue that my approach has the benefit - that I am going for - that a sticker pack is only downloaded once, even if multiple accounts exist. I have no concept of account specific sticker packs. Most of the state variables are just so I know what I have done and what not, e.g. preventing that we download every sticker pack, even if we did not even request it.

      But looking at the module again, I could probably move some code from the plugin to the module.

      I also thought that the upload of sticker packs should maybe happen per account to prevent leaking things like the HTTP host of other accounts, but initially I wanted to bundle the uploads together, like I did with the download.

      Did you think about what happens when in Gajim more than one account is active?

      Yes. I have multiple accounts linked to my Gajim, meaning that I do think about those interactions.

    • plugin.py itself should probably only be the interface to Gajim.

      • Handle events that originated from Gajim (sign-in etc)
      • Offer public methods which can be called from other plugins or Gajim

      the module should handle everything else.

      • Uploading to the account
      • Downloading from the account
      • Reacting to stanza events
      • Holding any account specific data

      Also it rather seems forced that you inform plugin.py via many events of everything that happens in the module.

      This should not mean you cant have a single store for all sticker packs. init the store in plugin.py, and afterwards simply modify the store from the module, you already have a ref to the plugin there and with that a ref to the store.

      What i like to do for something like that is using a Singleton, checkout AvatarStorage in avatar.py in Gajim. Singleton means you can simply do from sticker.store import Store and have it available everywhere without passing references around.

    • you binding to the decrypted-message-received GUI signal from Gajim, its meant as GUI signal meaning something in the GUI should happen.

      Its better you bind in the module via StanzaHandler to the message stanza with according priority, look for example in the omemo plugin module on how to use it.

    • Yeah, the age and differences in this XEP and my own thought up really show in the code now. I will try and do that.

      Edited by Alexander
    • Please register or sign in to reply
  • Alexander added 1 commit

    added 1 commit

    • ed35b4d7 - [stickers] Fix config issue on first start

    Compare with previous version

    • For now please remove #max_items from publish options, ejabberd does not support this field in publish options, and its a very widespread server.

      as workaround you could retrive the node configuration, and simply check what max_items is set to, and if you dont like it push a new configurion. see Pubsub module

      • set_node_configuration
      • get_node_configuration
    • Callback seems broken after upload

      Traceback (most recent call last):
        File "/home/lovetox/projects/gajim/gajim/common/ged.py", line 101, in raise_event
          if handler(*args, **kwargs):
        File "/home/lovetox/.local/share/gajim/plugins/stickers/plugin.py", line 365, in _on_sticker_pack_uploaded
          self._model_replace(StickerPackObject.from_sticker_pack(pack))
        File "/home/lovetox/.local/share/gajim/plugins/stickers/plugin.py", line 319, in _model_replace
          index = self._model_remove(pack.id_)
        File "/home/lovetox/.local/share/gajim/plugins/stickers/plugin.py", line 306, in _model_remove
          for index_ in range(model.get_n_items()):
      NameError: name 'model' is not defined
    • Should be fixed.

    • Please register or sign in to reply
  • Alexander added 5 commits

    added 5 commits

    • f74b010d - [stickers] Fix reference to undefined 'model'
    • 383ebd0b - [stickers] Big rework!
    • aaafcbb0 - [stickers] Lint
    • a2e8b664 - [stickers] Don't HTTP Upload when it's not available
    • 4a9662e1 - [stickers] Make name clearer

    Compare with previous version

  • mentioned in issue gajim#9315

  • Alexander added 5 commits

    added 5 commits

    • 7e2e669a - [stickers] Bump min version
    • fbbb2bc7 - [stickers] Previously forgotten implementation of get_configuration
    • 8d2957f3 - [stickers] Hopefully working implementation
    • 372d2c49 - [stickers] Bump version
    • 611a5c4c - [stickers] Configure node before publishing

    Compare with previous version

  • Now that I have got time again, I started working on this plugin again. When publishing on the PubSub node, the plugin now first checks if the max_items property is set to a sufficiently high number for all sticker packs and if not, it reconfigures the node.

    What I still have to do before I would say that I'm "done", is the following:

    • Find a better icon for the "upload and publish sticker pack" button
    • Test some more! I think that there are issues with Windows, but I am not sure about that one
    • Maybe add a setting to set the access model on the PubSub node
  • Nice to see you working on that Plugin again!

    Maybe send-to-symbolic would be a better icon (indicates sharing).

  • Alexander added 6 commits

    added 6 commits

    • b9611d5d - [stickers] Replace icons
    • 984d712f - [stickers] Use a better icon for 'publish'
    • 8e7ba153 - [stickers] Refactor how stickers get matched
    • 4f5ed6cd - [stickers] Fix rework
    • f3ff50d8 - [stickers] Prevent sticker packs from appearing twice
    • 54f89344 - [stickers] Implement a privacy toggle

    Compare with previous version

  • I have now implemented a setting for setting the access model of the stickers PubSub node and found a better icon. This plugin now implements everything from XEP-0449, except for the sharing of sticker packs and explicit dealing with restricted sticker packs, i.e. you can use them just like any other sticker pack.

    I also noticed that I left in a local file with which I have been generating the sticker packs' info.json file. I will remove that from the MR.

    The code has reached a point at which I would say that this plugin has all features I wanted it to have and that it is ready for testing outside of my own use.

    Except for bug fixes and code style fixes, which I plan on doing, I probably won't add any more code.

    • hi, i just looked over the code and in the stickers_module.py please replace all calls

      app.connections[self._account] -> self._client

      app.connections should not be used anymore, i want to get rid of it in the future.

    • I was just working on this and noticed that I use app.connections in one place to publish a sticker pack to all connected accounts. Is there already some way I could do that without app.connections that I missed or must that wait?

    • Please register or sign in to reply
  • Alexander added 3 commits

    added 3 commits

    • 0d1f82fa - [stickers] Remove leftover script
    • 8cf721b9 - [stickers] Lint files using pylint
    • e89bafc5 - [stickers] Replace app.connections[...]

    Compare with previous version

  • Please register or sign in to reply
    Loading