First Plugin
Running your first plugin
Before we start
It's recommended to review the Plugin Class Reference documentation or keep it open for reference while developing plugins.
Basic plugin structure
All .plugin files must include:
- Meta variables defined as plain strings (
__id__,__name__,__description__,__author__,__version__,__icon__,__min_version__) - A single class that inherits from
BasePlugin
Here's the most basic plugin template:
Creating simple Weather plugin
In this example, we'll create a plugin that provides weather information when a user sends a message prefixed with .wt.
We'll use the wttr.in API to fetch weather data.
Implementing network call and formatting
First, let's implement the functions to fetch and format weather data. They're quite boilerplate, so we won't look deep into it:
Third-Party Libraries
The requests library is used here for making HTTP requests. It is one of several third-party libraries that are pre-installed in the plugin environment. For a full list, see the Available Libraries page.
Hooking message send event
To intercept and modify messages, we implement the on_send_message_hook method in our plugin class:
To make your on_send_message_hook method actually get called by the plugin system, you need to register this hook. This is typically done in on_plugin_load by calling self.add_on_send_message_hook().
The on_send_message_hook method returns a HookResult with a MODIFY strategy, which means the message will be modified before sending. An empty HookResult won't modify the message.
Complete example (Initial)
Here's the complete implementation of the Weather plugin before performance enhancements:
Testing the Plugin
Try sending message like .wt in any chat. You should get something similar to this:
Performance Considerations
Fixing UI freeze
You may notice that the app freezes for a few seconds when using the plugin. This happens because the network call (requests.get) is a blocking I/O operation running on the UI thread. While the request is processing, the app cannot render anything.
To fix this issue, move blocking calls to a separate thread or queue to avoid blocking the UI thread. We can use client_utils.run_on_queue for the background network request and android_utils.run_on_ui_thread to post results back to the UI thread (e.g., to send the message or dismiss a dialog).
Additionally, we'll show a loading indicator using AlertDialogBuilder from alert.py while fetching data and then use client_utils.send_message to send the processed message.
Here's the improved version:
In this improved version:
- We import
AlertDialogBuilderfromalert. - The
__init__method initializesself.progress_dialog_builder. Theon_plugin_loadmethod is used to callself.add_on_send_message_hook(). - When
.wtis detected, we create andshow()anAlertDialogBuilderofALERT_TYPE_SPINNER. - The actual work (
_process_weather_request) is dispatched to a background queue usingrun_on_queue. _process_weather_requestperforms the network call. After getting the result, it schedules_send_message_and_dismiss_dialogon the UI thread usingrun_on_ui_thread._send_message_and_dismiss_dialogdismisses the progress dialog and then usesclient_utils.send_messageto send the weather information as a new message.- The original message sending is cancelled by returning
HookResult(strategy=HookStrategy.CANCEL).
This approach ensures the UI remains responsive while fetching data.
What's next?
To further develop your plugin skills, explore community-made plugins: