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
Pluginclass is inmodule_name/__init__.pywheremodule_nameis the name you defined in the questionnaire- Methods:
log(*args, msg_args=[], level=None): Log anything to console (argscan be any type, it will be cast to a string first),msg_argsa tuple or list of values to insert into the message with%s,%detc,levellevel 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_prefixput 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.beforeandafterare the full settings before and after the change,changeis 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
PLUGININFOfile. So you don't have to care about setting these up on your own. - Use
PeriodicJobsfromfrom .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 subclassPeriodicJoband define the method yourselfname ['']: name of the jobnbefore_start [None]: callable to call before the job is started. This can be used with thefirst_roundvariable 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_startto 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 enteredfoothe 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
initiatorandargsarguments 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 asinitiatorandargstring(argstringwill 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.urlopenas it allows to setheadersanddatadirectly, where when not definedheadersis auto filled with a common user agent. It returns aResponsewhich auto decodes the content toresponse.contentand returns a dict if possible with parsing json withresponse.jsonlog(*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 thelogmethod described in the main plugincommand(func):@commandwrapper 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
PLUGININFOyourself where you have to escape"chars and replace newlines with\nyou can useDESCRIPTIONinstead. This file will be minimized and put intoPLUGININFOduring release. - To release a new version use the
./releaserscript (it uses the fish shell which is not POSIX compatible so it won't work with bash)- It will minimize the
DESCRIPTIONand put it into thePLUGININFO - It will increase the version by
0.0.1by default. Change that if needed - After the release, the version will be
x.x.x.dev0
- It will minimize the