mirror of
				https://github.com/jlu5/PyLink.git
				synced 2025-11-04 00:47:21 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			167 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# 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`, and `args`.
 | 
						|
 | 
						|
1) **numeric**: The sender of the hook payload (normally a UID or SID).
 | 
						|
 | 
						|
2) **command**: The command name (hook name) of the payload. These are *always* UPPERCASE, and those starting with "PYLINK_" indicate hooks sent out by PyLink IRC objects themselves; i.e. they don't require protocol modules to handle them.
 | 
						|
 | 
						|
3) **args**: The hook data (args), a Python `dict`, with different data keys and values depending on the command given.
 | 
						|
 | 
						|
*Note:* 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 sends a TS value itself.
 | 
						|
 | 
						|
### Example syntax
 | 
						|
 | 
						|
The command `:42XAAAAAB PRIVMSG #dev :test` would result in the following raw hook data:
 | 
						|
 | 
						|
- `['42XAAAAAB', 'PRIVMSG', {'target': '#dev', '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 automatically 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': IrcChannel({'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
 | 
						|
 | 
						|
These following hooks, sent with their correct data keys, are required for PyLink's basic functioning.
 | 
						|
 | 
						|
- **ENDBURST**: `{}`
 | 
						|
    - The hook data here is empty.
 | 
						|
    - This payload should be sent whenever a server finishes its burst, with the SID of the bursted server as the sender.
 | 
						|
    - Plugins like Relay need this to know that the uplink has finished bursting all its users!
 | 
						|
 | 
						|
- **PYLINK_DISCONNECT**: `{}`
 | 
						|
    - This is sent to plugins by IRC object instances whenever their network has disconnected. The sender here is always **None**.
 | 
						|
 | 
						|
- **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 original data too for whatever reason. `olduserobj` represents a `classes.IrcUser` instance.
 | 
						|
    - Example payload:
 | 
						|
```
 | 
						|
{'olduser': IrcUser({'away': '',
 | 
						|
                     'channels': {'#chat'},
 | 
						|
                     'host': 'pylink.local',
 | 
						|
                     'ident': 'pylink',
 | 
						|
                     'identified': False,
 | 
						|
                     'ip': '0.0.0.0',
 | 
						|
                     'manipulatable': True,
 | 
						|
                     'modes': {('o', None)},
 | 
						|
                     'nick': 'PyLink-devel',
 | 
						|
                     'realhost': 'pylink.local',
 | 
						|
                     'realname': 'PyLink development server',
 | 
						|
                     'ts': 1452393682,
 | 
						|
                     'uid': '7PYAAAAAE'}),
 | 
						|
 'ts': 1452393899)}
 | 
						|
```
 | 
						|
 | 
						|
 | 
						|
## IRC command hooks
 | 
						|
 | 
						|
The following hooks represent regular IRC commands sent between servers.
 | 
						|
 | 
						|
### Basic commands
 | 
						|
 | 
						|
- **JOIN**: `{'channel': '#channel', 'users': ['UID1', 'UID2', 'UID3'], 'modes': [('n', None), ('t', None), ('k', 'somesecretkey')], 'ts': 1234567890}`
 | 
						|
    - This hook handles both SJOIN and JOIN commands, to make writing plugins slightly easier (they only need to listen to one hook).
 | 
						|
    - `channel` sends the channel name, `users` sends a list of joining UIDs, and `ts` returns the TS (an `int`) that we have for the channel.
 | 
						|
    - `modes` returns a list of parsed modes: `(mode character, mode argument)` tuples, where the mode argument is either `None` (for modes without arguments), or a string.
 | 
						|
    - The sender of this hook payload is IRCd-dependent, and is determined by whether the command was originally a SJOIN or regular JOIN - SJOIN is only sent by servers, and JOIN is only sent by users.
 | 
						|
    - For IRCds that support joining multiple channels in one command (`/join #channel1,#channel2`), consecutive JOIN hook payloads of this format will be sent (one per channel).
 | 
						|
 | 
						|
- **KICK**: `{'channel': '#channel', 'target': 'UID1', 'text': 'some reason'}`
 | 
						|
    - `text` refers to the kick reason. The `target` and `channel` fields send the target's UID and the channel they were kicked from, and the sender of the hook payload is the kicker.
 | 
						|
 | 
						|
- **KILL**: `{'target': killed, 'text': args[1], 'userdata': data}`
 | 
						|
    - `text` refers to the kill reason. `target` is the target's UID.
 | 
						|
    - The `userdata` key may include an `IrcUser` instance, depending on the IRCd. On IRCds where QUITs are explicitly sent (InspIRCd), `userdata` will be `None`. Other IRCds do not explicitly send QUIT messages for KILLed clients, so the daemon must assume that they've quit, and deliver their last state to plugins that require this info.
 | 
						|
 | 
						|
- **MODE**: `{'target': '#channel', 'modes': [('+m', None), ('+i', None), ('+t', None), ('+l', '3'), ('-o', 'person')], 'oldchan': IrcChannel(...)}`
 | 
						|
    - `target` is the target the mode is being set on: it may be either a channel (for channel modes) OR a UID (for user modes).
 | 
						|
    - `modes` is a list of prefixed parsed modes: `(mode character, mode argument)` tuples, but with `+/-` prefixes to denote whether each mode is being set or unset.
 | 
						|
    - For channels, the `oldchan` key is also sent, with the state of the channel BEFORE this MODE hook was processed.
 | 
						|
        - One such use for this is to prevent oper-override hacks: checks for whether a sender is opped have to be done before the MODE is processed; otherwise, someone can simply op themselves and circumvent this detection.
 | 
						|
 | 
						|
- **NICK**: `{'newnick': 'Alakazam', 'oldnick': 'Abracadabra', 'ts': 1234567890}`
 | 
						|
 | 
						|
- **NOTICE**: `{'target': 'UID3', 'text': 'hi there!'}`
 | 
						|
    - *Note:* `target` can not only be a channel or a UID, but also a channel with a prefix attached (e.g. `@#lounge`). These cases should not be overlooked!
 | 
						|
 | 
						|
- **PART**: `{'channels': ['#channel1', '#channel2'], 'text': 'some reason'}`
 | 
						|
    - `text` can also be an empty string, as part messages are *optional* on IRC.
 | 
						|
    - Unlike the JOIN hook, multiple channels can be specified in a list for PART. This means that a user PARTing one channel will cause a payload to be sent with `channels` as a one-length *list* with the channel name.
 | 
						|
 | 
						|
- **PRIVMSG**: `{'target': 'UID3', 'text': 'hi there!'}`
 | 
						|
    - Ditto with NOTICE: `target` can be a channel or a UID, or a channel with a prefix attached (e.g. `@#lounge`).
 | 
						|
 | 
						|
- **QUIT**: `{'text': 'Quit: Bye everyone!'}`
 | 
						|
    - `text` corresponds to the quit reason.
 | 
						|
 | 
						|
- **SQUIT**: `{'target': '800', 'users': ['UID1', 'UID2', 'UID6'], 'name': 'some.server'}`
 | 
						|
    - `target` is the SID of the server being split, while `name` is the server's name.
 | 
						|
    - `users` is a list of all UIDs affected by the netsplit.
 | 
						|
 | 
						|
- **TOPIC**: `{'channel': channel, 'setter': numeric, 'text': 'Welcome to #Lounge!, 'oldtopic': 'Welcome to#Lounge!'}`
 | 
						|
    - `oldtopic` denotes the original topic, and `text` indicates the new one being set.
 | 
						|
    - `setter` is the raw sender field given to us by the IRCd; it may be a `nick!user@host`, a UID, a SID, a server name, or a nick. This is not processed any further.
 | 
						|
 | 
						|
- **UID**: `{'uid': 'UID1', 'ts': 1234567891, 'nick': 'supercoder', 'realhost': 'localhost', 'host': 'admin.testnet.local', 'ident': ident, 'ip': '127.0.0.1'}`
 | 
						|
    - This command is used to introduce users; the sender of the message should be the server bursting or announcing the connection.
 | 
						|
    - `ts` refers to the user's signon time. 
 | 
						|
 | 
						|
### Extra commands (where supported by the IRCd)
 | 
						|
 | 
						|
- **AWAY**: `{'text': text}`
 | 
						|
    - `text` denotes the away reason. It is an empty string (`''`) when a user is unsetting their away status.
 | 
						|
 | 
						|
- **CHGHOST**: `{'target': 'UID2', 'newhost': 'some.silly.host'}`
 | 
						|
    - SETHOST, CHGHOST, and any other events that cause host changes should return a CHGHOST hook payload. The point of this is to track changes in users' hostmasks.
 | 
						|
 | 
						|
- **CHGIDENT**: `{'target': 'UID2', 'newident': 'evilone'}`
 | 
						|
    - SETIDENT and CHGIDENT commands, where available, both share this hook name.
 | 
						|
 | 
						|
- **CHGNAME**: `{'target': 'UID2', 'newgecos': "I ain't telling you!"}`
 | 
						|
    - SETNAME and CHGNAME commands, where available, both share this hook name.
 | 
						|
 | 
						|
- **INVITE**: `{'target': 'UID3', 'channel': '#myroom'}`
 | 
						|
 | 
						|
- **KNOCK**: `{'text': 'let me in please!', 'channel': '#myroom'}`
 | 
						|
    - This is not actually implemented by any protocol module as of writing.
 | 
						|
 | 
						|
- **SAVE**: `{'target': 'UID8', 'ts': 1234567892, 'oldnick': 'Abracadabra'}`
 | 
						|
    - For protocols that use TS6-style nick saving. During nick collisions, instead of killing the losing client, servers that support SAVE will send such a command targeting the losing client, which forces that user's nick to their UID.
 | 
						|
 | 
						|
 | 
						|
## Hooks that don't map to IRC commands
 | 
						|
Some hooks do not map directly to IRC commands, but to events that protocol modules should handle.
 | 
						|
 | 
						|
- **CLIENT_SERVICES_LOGIN**: `{'text': 'supercoder'}`
 | 
						|
    - This hook is sent whenever a user logs in to a services account, where `text` is the account name. The sender of the hook is the UID of the user logging in.
 | 
						|
 | 
						|
- **CLIENT_OPERED**: `{'text': 'IRC_Operator'}`
 | 
						|
    - This hook is sent whenever an oper-up is successful: when a user with umode `+o` is bursted, when umode `+o` is set, etc.
 | 
						|
    - The `text` field denotes the oper type (not the SWHOIS), which is used for WHOIS replies on different IRCds.
 | 
						|
 | 
						|
## Commands handled WITHOUT hooks
 | 
						|
At this time, commands that are handled by protocol modules without returning any hook data include PING, PONG, and various commands sent during the initial server linking phase.
 |