3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-01 09:19:23 +01:00

docs: add part of hooks-reference, mention topics and modes in protocol module spec, some more changes

ref: #113.
This commit is contained in:
James Lu 2015-12-26 16:44:22 -08:00
parent 11b8821228
commit e4815a72e1
4 changed files with 116 additions and 8 deletions

View File

@ -1,23 +1,24 @@
## PyLink Developer Documentation
# PyLink Developer Documentation
Please note that as PyLink is still in its development phase, the API is subject to change.
Any documentation here is provided for reference only.
It is also really incomplete (contributors welcome!)
### Introduction
## Introduction
PyLink is an a modular, plugin-based IRC PseudoService framework. It uses swappable protocol modules and a hook-based system for calling plugins, allowing them to function regardless of the IRCd used.
<img src="core-structure.png" width="50%" height="50%">
### Contents
## Contents
- [Writing plugins for PyLink](writing-plugins.md)
- [PyLink protocol module specification](pmodule-spec.md)
#### Future topics (not yet available)
- [Using PyLink's utils module](using-utils.md)
### WIP topics
- [PyLink hooks reference](hooks-reference.md)
### Future topics (not yet available)
- [Writing tests for PyLink modules](writing-tests.md)

View File

@ -0,0 +1,63 @@
# PyLink hooks reference
## Introduction
In PyLink, protocol modules communicate with plugins through a system of hooks. This has the benefit of being IRCd-independent, allowing most plugins to function regardless of the IRCd being used.
Each hook payload is formatted as a Python `list`, with three arguments `(numeric, command, args)`:
1) **numeric**: The sender of the message (UID).
2) **command**: The command name (hook name) of the payload. These are *always* UPPERCASE, and those starting with "PYLINK_" indicate hooks sent out by IRC objects themselves, that don't require protocol modules to send.
3) **args**: The hook data (args), a Python `dict`, with different data keys depending on the command given.
Note that the `ts` key is *automatically added* (using the current time) to all hook data dicts that don't include it - such a key should only be provided if the command the uplink IRCd send has a TS value itself.
### Example syntax
The command `:42XAAAAAB PRIVMSG #endlessvoid :test` would result in the following raw hook data:
- `['42XAAAAAB', 'PRIVMSG', {'target': '#endlessvoid', 'text': 'test', 'ts': 1451174041}]`
On UnrealIRCd, because SETHOST is mapped to CHGHOST, `:GL SETHOST blah` would return the raw hook data of this (with the nick converted into UID by the UnrealIRCd protocol module):
- `['001ZJZW01', 'CHGHOST', {'ts': 1451174512, 'target': '001ZJZW01', 'newhost': 'blah'}]`
Some hooks, like MODE, are more complex and can include the entire state of a channel! This will be further described later. `:GL MODE #chat +o PyLink-devel` is converted into (pretty-printed for readability):
- ```
['001ZJZW01',
'MODE',
{'modes': [('+o', '38QAAAAAA')],
'oldchan': {'modes': set(),
'prefixmodes': {'admins': set(),
'halfops': set(),
'ops': set(),
'owners': set(),
'voices': set()},
'topic': '',
'topicset': False,
'ts': 1451169448,
'users': {'38QAAAAAA', '001ZJZW01'}},
'target': '#chat',
'ts': 1451174702}]
```
## Core hooks
The following hooks are required for PyLink's basic functioning.
- **ENDBURST**: `{}` - Although the hook data is empty, this should be sent whenever a server finishes its burst. The sender should be the server that finishes bursting.
<br>
Plugins like Relay need this to know that the uplink has finished bursting all its users!
- **PYLINK_DISCONNECT**: `{}` - This is sent to plugins by the IRC objects when their network has disconnected. The sender (numeric) is **None** in this case.
- **PYLINK_SPAWNMAIN**: `{'olduser': olduserobj}` - This is sent whenever `Irc.spawnMain()` is called to (re)spawn the main PyLink client, for example to rejoin it from a KILL. It basically tells plugins that the UID of the main PyLink client has changed, while giving them the old data too.
## IRC command hooks
The following hooks represent regular IRC commands sent between servers.
<br><br><br>
(under construction)

View File

@ -90,10 +90,54 @@ optional, and defaults to the one we've stored in the channel state if not given
- **`updateClient`**`(self, source, field, text)` - Updates the ident, host, or realname of a PyLink client. `field` should be either "IDENT", "HOST", "GECOS", or
"REALNAME". If changing the field given on the IRCd isn't supported, `NotImplementedError` should be raised.
## Special variables
## Things to note
### Special variables
A protocol module should also set the following variables in their protocol class:
- `self.casemapping`: set this to `rfc1459` (default) or `ascii` to determine which case mapping the IRCd uses.
- `self.hook_map`: this is a `dict`, which maps non-standard command names sent by the IRCd to those that PyLink plugins use internally.
- Examples exist in the [UnrealIRCd](https://github.com/GLolol/PyLink/blob/0.5-dev/protocols/unreal.py#L22) and [InspIRCd](https://github.com/GLolol/PyLink/blob/0.5-dev/protocols/inspircd.py#L24) modules.
### Topics
When receiving or sending topics, there is a `topicset` attribute in the IRC channel (IrcChannel) object that should be set **True**. It simply denotes that a topic has been set in the channel at least once.
(Relay uses this so it doesn't overwrite topics with empty ones during burst, when a relay channel initialize before the uplink has sent the topic for it)
### Mode formats
Modes are stored a special format in PyLink, different from raw mode strings in order to make them easier to parse. Mode strings can be turned into mode *lists*, which are used to both represent mode changes in hooks, and when storing them internally.
`utils.parseModes(irc, target, split_modestring)` is used to convert mode strings to mode lists, where `irc` is the IRC object, `target` is the channel or user the mode is being set on, and `split_modestring` is the string of modes to parse, *split at each space* (really a list).
- `utils.parseModes(irc, '#chat', ['+tHIs', '*!*@is.sparta'])` would give:
- `[('+t', None), ('+H', None), ('+I', '*!*@is.sparta'), ('+s', None)]`
Also, it will automatically convert prefix mode targets from nicks to UIDs, and drop invalid modes
- `utils.parseModes(irc, '#chat', ['+ol', 'invalidnick'])`:
- `[]`
- `utils.parseModes(irc, '#chat', ['+o', 'GLolol'])`:
- `[('+o', '001ZJZW01')]`
Then, the parsed mode lists can be applied to channel using `utils.applyModes(irc, target, parsed_modelist)`.
Modes are stored in channels and users as sets: `(userobj or chanobj).modes`:
- ```
<+GLolol> PyLink-devel, eval irc.users[source].modes
<@PyLink-devel> {('i', None), ('x', None), ('w', None), ('o', None)}
<+GLolol> PyLink-devel, eval irc.channels['#chat'].modes
<@PyLink-devel> {('n', None), ('t', None)}
```
*With the exception of channel prefix modes* (op, voice, etc.), which are stored as a dict of sets in `chanobj.prefixmodes`:
- ```
<@GLolol> PyLink-devel, eval irc.channels['#chat'].prefixmodes
<+PyLink-devel> {'ops': set(), 'halfops': set(), 'voices': {'38QAAAAAA'}, 'owners': set(), 'admins': set()}
```
When a certain mode (e.g. owner) isn't supported on a network, the key still exists in `prefixmodes` but is simply unused.

View File

@ -10,7 +10,7 @@ Plugins have three main ways of communicating with IRC: hooks, WHOIS handlers, a
### Hooks
Hooks are probably the most versatile form of communication. Each hook payload is formatted as a Python `dict`, with different data keys depending on the command.
Hooks are probably the most versatile form of communication. The data in each hook payload is formatted as a Python `dict`, with different data keys depending on the command.
For example, a `PRIVMSG` payload would give you the fields `target` and `text`, while a `PART` payload would only give you `channels` and `reason` fields.
There are many hook types available (one for each supported IRC command), and you can read more about them in the [PyLink hooks reference](hooks-reference.md).