Nicotine+ Plugin Template
Template for easy and fast Nicotine+ plugins development
Before developing read the Features / Usage section to see how it works. Other than that you can take a look at my N+ plugins, the N+ documented base plugin and the default plugins to find the hooks and what they do.
- You can use more than one module
- Easier Version management and update checker
- Reload command so you don't need to click around during development
- Simple to use periodic jobs
- Various convenience methods
- Auto-prefix commands
Clone the repo, install cookiecutter and run the wizard. You'll get a
git clone https://github.com/Nachtalb/nicotine_plus_plugin_template.git
cd nicotine_plus_plugin_template
pip install cookiecutter
cookiecutter.
Python version 3.8 and up
- The main
Plugin
class is inmodule_name/__init__.py
wheremodule_name
is the name you defined in the questionnaire- Methods:
log(*args, msg_args=[], level=None)
: Log anything to console (args
can be any type, it will be cast to a string first),msg_args
a tuple or list of values to insert into the message with%s
,%d
etc,level
level string to define where the message will be posted to (console, chat, info window etc. see N+ base plugin linked above for more info,with_prefix
put plugin name as a prefix to the messageinit()
: init method for the plugin. When overridden first this should be a super callpre_stop()
: a method called before the plugin is disabled or N+ is quiterror_window(args*, msg_args=[])
: Wrapper forlog()
to show an error windowinfo_window(args*, msg_args=[])
: Wrapper forlog()
to show an info windowsettings_changed(before, after, change)
: Method called after the user changed some settings.before
andafter
are the full settings before and after the change,change
is a dict like{'before': ..., 'after': ...}
which only contains the delta
- Methods:
- The plugin will automatically get any information about the repository, command prefix, version number, plugin name etc from the
PLUGININFO
file. So you don't have to care about setting these up on your own. - Use
PeriodicJobs
fromfrom .core.threading import PeriodicJob
- Arguments:
delay [1]
: how fast to run in seconds. Can be a callable that returns an intupdate [None]
: function to call, it can be none if you subclassPeriodicJob
and define the method yourselfname ['']
: name of the jobnbefore_start [None]
: callable to call before the job is started. This can be used with thefirst_round
variable to chain start jobs
- Methods:
stop(wait=True)
: Stop the job, by default, waits for the job to endpause()
: Pause the job.resume()
: Resume the job
- Variables:
self.first_round
: A thread event that will be set after the job has run the first time. Can be used together withbefore_start
to chain jobsself.last_run
: Unix timestamp of last job execution
- Arguments:
-
/prefix-update
: The plugin comes with an automatic update check from the start. It will periodically ask GitHub for any new updates and will prompt the user. This can be disabled with a checkbox in the plugin preferences. -
/prefix-reload
: Reload the plugin code without the need to click through the settings menu -
During the development of the project the commands will be prefixed with an additional
d
. So if you enteredfoo
the command will be/dfoo
. When releasing a new version this will automatically change to/foo
The @command
decorator from from .core.utils import command
is designed to make life easier. Not only eases the registration of commands but also parses user arguments and makes sure that Nicotine+ doesn't freeze.
- No interaction with
__publiccommands__
or__privatecommands__
needed. - The decorator makes the
initiator
andargs
arguments optional thus you can call the methods from other places in your script without much thought. You have to use only keyword arguments tho otherwise the first two positional arguments will be interpreted asinitiator
andargstring
(argstring
will be parsed toargs
).
In this example, the command prefix was set to ab
from .core.base import BasePlugin
from .core.utils import command
class Plugin(BasePlugin):
# No arguments are given, infer command name "foo". With the prefix said
# above the command will be /ab-foo. # "args" is a list of parsed
# arguments given by the user - numbers will be int or float.
@command
def foo(self, args):
...
# "initiator" where the command was run from. This will either be the user
# name of the partner in the private chat or the room name in the public
# chat.
@command
def foo(self, initiator):
...
# The user may use the command like "/aa-foo a_flag", "/aa-foo a_flag=True"
# or "/aa-foo -a_flag" to set the argument to True
@command
def foo(self, a_flag=False):
...
# The user may use the command like "/aa-foo a_int=123" to define a number.
# Because we set the type to "int" floats like "12.3" or strings like "bar"
# will be ignored.
@command
def foo(self, a_int: int = None):
...
# By default commands are available in both private and public chat. We can
# disable it for either chat - here we disable it for public chats.
@command(public=False)
def foo(self):
...
# When the command name is inferred "_" will be replaced with "-". This
# making the command "/ab-foo-bar"
@command()
def foo_bar(self):
...
# The command doesn't need to be foo - it can be whatever you wish.
@command('custom-named')
def foo(self):
...
# Disable the prefix. The command will be "/foo"
@command(with_preix=False)
def foo(self):
...
# No command is created if the command name is inferred from the method name and
# starts with "_"
@command()
def _bar(self):
...
# Usually the commands are run in a separate thread to prevent Nicotine+
# from freezing during the execution. You can disable that behaviour.
@command(threaded=False)
def foo(self):
...
from .core.utils import ...
get(url, data=None, headers={}, timeout=30)
a easy to use version ofurllib.request.urlopen
as it allows to setheaders
anddata
directly, where when not definedheaders
is auto filled with a common user agent. It returns aResponse
which auto decodes the content toresponse.content
and returns a dict if possible with parsing json withresponse.json
log(*msg, msg_args=[], level=None, prefix=None)
: Simple to use logging method that accepts anything. You can set a prefix withprefix=
the other arguments work the same as thelog
method described in the main plugincommand(func)
:@command
wrapper as specified in aboves sectionstr2num(string)
: A simple to use string to int/float converted without error throwing. If the string cannot be parsed to a number the string will be returned again.startfile(file)
: Start a file with the default application on the pc
- Instead of writing a plugin description painstakingly into
PLUGININFO
yourself where you have to escape"
chars and replace newlines with\n
you can useDESCRIPTION
instead. This file will be minimized and put intoPLUGININFO
during release. - To release a new version use the
./releaser
script (it uses the fish shell which is not POSIX compatible so it won't work with bash)- It will minimize the
DESCRIPTION
and put it into thePLUGININFO
- It will increase the version by
0.0.1
by default. Change that if needed - After the release, the version will be
x.x.x.dev0
- It will minimize the