gs-new-hook message_type preemptive icase
regex pattern
[proc | #f]
gs-new-hook #message_type preemptive icase regex serial pattern [proc | #f] |
This procedure is scary. However, you normally do not need to call it directly. You would use a wrapper function.
message_type is the kind of message for which we wish to set a hook. In other words, it is the hook type. message_type may also be a server numeric. Server numerics are 3 digit numbers originating from IRC servers. The generic hook type "RAW_IRC" may be used for all event types including server numerics. There are about 50 hook types currently defined. If the sharp sign '#' precedes message_type, the hook will be assigned the serial number serial.
preemptive, icase, and regex
are each either true or false.
If preemptive is true, a preemptive hook will be set.
If icase is true, there will be no upper/lower case distinction
when patterns are matched.
If regex is true, pattern will be considered to be a
POSIX regular expression. If regex is false, pattern
will be considered to be a shell (bash) pattern, and pattern matching
will be done shell like.
serial is the serial number of the hook.
pattern is the expression against which we want to match messages. In other words, messages of type message_type will trigger this hook if the messages match pattern. If regex was false, we match against a shell expression. All shell metacharacters are then allowed in the pattern. Recall:
? matches any single character
* matches any sequence of letters (including blanks) ^ excludes a character or set of characters [] a set of optional letters |
If regex was true, we match against a modern regular expression "RE" [see regex(7)]. RE's are more powerful than shell expressions. Let's recall a few things about RE's. Let r be an extended regular expression. Then:
x | matches the character 'x' |
. | matches any single charter |
^r | matches r, but only at the beginning of the message |
r$ | matches r, but only at the end of the message |
r? | matches 0 or 1 occurrence of r |
r* | matches any sequence of 0 or more matches |
r+ | matches a sequence of 1 or more r's |
r{4} | matches exactly 4 r's |
r{4,} | matches 4 or more r's |
r{4,10} | matches between 4 and 10 r's |
[abT-W0-5] | matches any character in the character class: an 'a', a 'b', any letter between 'T' and 'W', and any digit from 0 through 5. |
[^abT-W0-5] | matches any characters that is not in the class |
r|s | matches either r or the regular expression s |
(r) | matches r; parentheses are used to group RE's |
A discussion of regular expressions is beyond the scope of this document.
See regex(7) for more details.
"^([^ ]+ ){3}[^a-z0-9]{10,}$" matches a message whose first 3 words
are followed by a portion that is at least 10 characters long. The portion
contains neither lowercase characters nor digits.
E.g. "irc.eskimo.com,6667 xtr!meyo@another.net #test THIS IS-A-TEST!".
"^([^ ]+ ){3}[^a-zA-Z0-9]{4,}$" matches a message whose first 3 words
are followed by a portion that is at least 4 characters long. The portion
contains no alphabetic characters and no digits.
E.g. "irc.eskimo.com,6667 xtr!meyo@another.net #test !&%$)?(
$,;%"
".*[!?]{5,}" matches a message with excessive !'s or/and ?'s or a combination of both (at least 5). E.g."irc.eskimo.com,6667 xtr!meyo@another.net #test anyone home!!??!!!??? Speak up."
In modern regular expressions, ^.[$()|*+?{\ are special
characters. In shell expressions, ?*^[] are special characters.
If a special character occurs in a pattern, the character has to be quoted,
escaped, back slashed. (Note that special characters sometimes loose their
special meaning, e.g. ^ inside [], and thus then need not be quoted.) Two
backslashes are required. The following note is curled from the Guile Reference
Manual.
Very important: Using backslash escapes in Guile source code (as in Emacs Lisp or C) can be tricky, because the backslash character has special meaning for the Guile reader. For example, if Guile encounters the character sequence `\n' in the middle of a string while processing Scheme code, it replaces those characters with a newline character. Similarly, the character sequence `\t' is replaced by a horizontal tab. Several of these escape sequences are processed by the Guile reader before your code is executed. Unrecognized escape sequences are ignored: if the characters `\*' appear in a string, they will be translated to the single character `*'. This translation is obviously undesirable for regular expressions, since we want to be able to include backslashes in a string in order to escape regexp metacharacters. Therefore, to make sure that a backslash is preserved in a string in your Guile program, you must use two consecutive backslashes: (define Info-menu-entry-pattern (make-regexp "^\\* [^:]*"))The string in this example is preprocessed by the Guile reader before any code is executed. The resulting argument to make-regexp is the string `^\* [^:]*', which is what we really want. This also means that in order to write a regular expression that matches a single backslash character, the regular expression string in the source code must include four backslashes. Each consecutive pair of backslashes gets translated by the Guile reader to a single backslash, and the resulting double-backslash is interpreted by the regexp engine as matching a single backslash character. Hence: (define tex-variable-pattern (make-regexp "\\\\let\\\\=[A-Za-z]*")) |
If proc is specified, then proc is any Scheme procedure that takes two arguments:
wrapper function | description |
gs-shell-hook type pattern proc | pattern is a shell expression; differentiate between
upper and lower case characters when matching. |
gs-shell-hook #type serial pattern proc | dito, but assign the serial number to the hook |
gs-regex-hook type pattern proc | pattern is a modern regex; case sensitive matching |
gs-regex-hook #type serial pattern proc | dito, but also use the serial number serial |
gs-shell-preempt type pattern proc | set a preemptive hook |
gs-shell-preempt #type serial pattern proc | dito, but assign serial number |
gs-regex-preempt type pattern proc | set a preemptive hook; pattern is a modern regexp |
gs-regex-preempt #type serial pattern proc | dito; assign the serial number serial |
gs-ishell-hook type pattern proc | same as the corresponding procedures above.
However, there will be no regard to case when messages are matched against the given patterns. |
gs-ishell-hook #type serial pattern proc | |
gs-iregex-hook type pattern proc | |
gs-ishell-preempt type pattern proc | |
gs-ishell-preempt #type serial pattern proc | |
gs-iregex-preempt type pattern proc | |
gs-iregex-preempt #type serial pattern pro |
gs-add-hook is equivalent to gs-ishell-hook.
gs-preempt is equivalent to gs-ishell-preempt.
gs-on is an obsolete pseudonym for gs-ishell-hook.
A serial number is a whole number between INT_MIN (-2147483648?) and INT_MAX (2147483647?) as defined in /usr/include/limits.h . Even when an event is not explicitly assigned a serial number, SPX silently assigns a serial number of zero to it.
The order in which hooks are triggered is determined by the values of their serial numbers: the priority of activation increases with decreasing serial number. Of non zero serial numbers, INT_MIN has the highest priority; INT_MAX, the least. Hooks having a serial number of zero are always triggered last. By assigning a zero serial number, you override the default action that the client normally takes for the event. That is, SPX will consider the event consumed as soon as it finishes working the hook.
Note also, that for messages from an IRC server, hooks on ``RAW_IRC'' are always checked as soon as a line is received from the IRC server. That happens before client parses the message, and a long time before before all other hook types are checked for matches. ``RAW_IRC'', especially when used with preemptive hooks, offers the user the possibility of seeing and possibly catching a message before the IRC client does.
(gs-shell-hook 333 "*" #f); Topic for #44th was set by ir on Sun Feb 14...
2. Throw people out of the channel
who use excessive CAPS and too many !'s and ?'s. Note the use of serial
numbers. Without them the messages wouldn't appear on your screen.
(let()
(define (kick_lamer lamer channel reason window)
(if(channel:op? (gs-channel2 channel window))
; are you channel op?
(gs-execute window "kick " channel
" " lamer " " reason)))
(define (no-CAPS-please m w)
(kick_lamer ($nick m) ($dest m) "Turn off the
damn CAPS" w))
;; message or notice is longer than 8 chars but contains
no lowercase characters and no digits:
(gs-regex-hook "#public_msg" 001 "^([^ ]+ ){3}[^a-z0-9]{8,}$"
no-CAPS-please)
(gs-regex-hook "#public_notice" 001 "^([^ ]+ ){3}[^a-z0-9]{8,}$"
no-CAPS-please)
;;EXcessive !!!! or ?????? or a combination of both (at
least 5 together):
(gs-regex-hook "#public_msg" 001 ".*[!?]{5,}" (lambda(m w)
(kick_lamer ($nick m) ($dest m) "Calm down!"
w))))
2b. Upon joining the channel #test on the Undernet, we do not want to see who created the channel.
(gs-add-hook 312 "*.undernet.org,* * * #test *"); #44th was created on Sun Feb. 14...3. Let's ignore all flooders from aol.com for 40 minutes. If were to ignore manually, we would have to type something like "/ignore *!*user@host.domain msg for 40 minutes''. We shall use the procedure gs-exec to execute the same command from within the Scheme interpreter.
(gs-add-hook "#FLOOD_PRIVATE" 100 "* *aol.com" (lambda(m w) (gs-execute w "ignore *!*" ($userhost m) " for 40 minutes"))))
4. Let's do something silly: prevent the user from signing off port
6665 of any Undernet servers. Just ring the bell and stay put:
(gs-preempt "signoff" "*.undernet.org,6665 *" (lambda(m w) (gs-echo "Sorry, cannot sign off 'tis server!" w) (gs-bell) #t))
5. If we are ignoring private messages from somebody and they ask
a question in a channel, we ban them if we're chan op. Also ignore the
question. Recall that channel names begin with either '&' or '#'.
(gs-preempt "RAW_IRC" "* * PRIVMSG [&#]* *\\?*" (lambda(m w) (define from (next-word m 1)) (if(gs-ignored? from 'msg) ;;if they match any msg ignore patterns (let ((channel (next-word m 3)) (sender ($userhost m))) (if(channel:op? (gs-channel2 channel w)) ;; if we are we chan op (gs-execute w "mode " channel " +b *!*" sender)) #t))));; return true to cancel the message
Gs-delete-hook directly calls the quoted ON command with its arguments. Type '/help on' for details. The ON command is used as follows to delete a hook:
command | effect |
ON -del type | removes every hook of the specified type that does not have a serial number. |
ON -del #type | removes all hooks of the given type if they have a non zero serial number. |
ON -del type pattern | removes all hooks of type type for which pattern is exactly the specified pattern. If icase is true for a hook, strcasecmp(3) is called to compare the patterns. Otherwise, strcmp(3) is used. |
ON -del #type ser_no | removes all hooks of type type which have the given serial number. |
ON -del #type ser_no pattern | removes all hooks having the given serial number and whose patterns are exactly the same as the given pattern. If icase is true for a hook, strcasecmp(3) is called to compare the patterns. Otherwise, strcmp(3) is used. |
If we elect not to use gs-delete-hook, then we have to pass the ON command to the procedure gs-exec:
(gs-exec "on -del <arguments as in the table above>")
The arguments to ON -del are exactly the same as those that were
passed to gs-XXX-hook.
As an example, let's suppose we no longer wish to ignore those from
aol.com who flood us. We many issue any of the following:
(gs-delete-hook "#flood_private" 100); removes all hooks on flood_private which have the serial number 100.
(gs-exec "on -del #flood_private 100 * *aol.com"); same as above but only if pattern is "* *aol.com *".
As a further example, if we decided to start seeing who set channel topic, the following would do:
(gs-exec "on -del 333")
(gs-remove-preempt "RAW_IRC * * PRIVMSG [&#]* *\\?")
(gs-delete-preempt "#flood_private" 100)
servername,portmessage
servername is the name of the server to which you are connected.
port is the port of the connection to the server.
Thus it is possible to restrict matches to particular servers!
message is the message part of the line which was received from
the IRC server. If a hook is set on a server numeric or on the hook type
RAW_IRC, message is raw IRC protocol, unchanged from how we received
it. Otherwise, the value of message depends upon the hook type.
To illustrate this, let's say we got a message from channel #lameric
on the IRC server irc.primenet.com, port 6667. Let the sender of the message
be wale!wm@f2.haha.com and the text of message, "I think I am a little
high!". A hook of type "PUBLIC_MSG" would then receive the following message:
irc.primenet.com,6667 wale!wm@f2.haha.com #lameric I think I am a little high!
It is up to the action bound to the hook to extract whatever it needs from the above message. In particular, if m is a private message/channel message, then without using any of the elaborate Guile string functions, the message can be split as follows:
server := (substring m 0 (string-index m #\,)) sender := (next-word m 1) nickname := (sender2nick (next-word 1)) channel := (nex-word m 2) msg := (string-rest m 3)To see raw IRC server messages, /SET show_raw_msg on.
Example: Attempt to echo back all notices from nick BlowFish on channel #lamerz. The IRC protocol does not allow this so SPX probably won't do it.
(gs-on "#public_notice" -100 "* blowfish!* #lamerz *" (lambda (m w) (gs-execute w "notice " ($nick m) " " ($msg m))))With the exception of RAW_IRC and numeric replies, the words in message generally have the following meanings:
$0 | server,port (e.g. "irc.eskimo.com,6667") |
$1 | sender of the message (nick!username@host.domain). Any tilde '~' in the user name has already been removed. |
$2 | destination of the message (channel or nickname) |
Depending on the message type, additional components may
be available as listed in the following table. Hooks not listed in the
table take the raw, unparsed server message.
Hook type | Pos | Position description |
JOIN
PART |
$1
$2 |
person who joined/left the channel
channel name |
NICK | $1
$2 |
person who changed nickname (nick!user@host)
new nickname |
QUIT | $2- | Sign off message |
ACTION
PRIVATE_MSG PUBLIC_MSG PRIVATE_NOTICE PUBLIC_NOTICE SERVER_NOTICE |
$3- | message text |
CTCP_REPLY | $3- | <command name> <text of the reply> |
INVITE | $3 | channel |
TOPIC | $2
$3- |
channel
new topic |
MODE | $2
$3- |
nickname or channel of mode change
the modes |
KICK | $1
$2 $3 $4- |
kicker!user@host
person kicked channel reason |
CTCP_SED | $3- | the undecoded message (PRIVMSG is later called after decoding) |
CTCP_ALL
CTCP_UNKNOWN |
$3- | <Command name> <arguments>
E.g. (gs-add-hook "CPCP_ALL" "* * * SOUND *") ... =>Commnand name=SOUND, arguments=all else after "SOUND" |
CTCP_HELP
CTCP_PING |
$1
$2 $3- |
sender
destination arguments |
CTCP_FINGER
CTCP_VERSION CTCP_USERINFO CTCP_TIME CTCP_SOURCE CTCP_CLIENTINFO |
$3- | nothing |
FLOOD_PRIVATE | $1
$2 $3- |
flooder (nick!username@host)
your nickname nothing |
FLOOD_PUBLIC | $1
$2 |
the flooder
channel |
DCC_SEND_REQUEST | $3- | assumption: filename inaddr port cksum ... |
DCC_CHAT_REQUEST | $3
$4 |
inaddr
port |
(gs-shell-hook "private_msg" "* *!~* * *\\?*" (lambda(m
w)
(define the-command (string-append
"/notice " ($nick m) " Run identd first!"))
(gs-exec the-command w)))
However, using RAW_IRC works:
(gs-shell-hook "raw_irc" "* *!~* PRIVMSG
[^&#]* *\\?*" (lambda(m w)
(define the-command
(string-append "/notice
" ($nick m) " Run identd first!"))
(gs-exec the-command w)))
The pattern "*!~*" matches nick!~name@host.domain (the sender of the
message). Channel names start with either "#" or "&", hence the pattern
[^&#]* is used to catch questions that aren't destined for a channel.
Hook type | Trigger | |
ACTION | CTCP action (public and private), usually as a result of somebody doing /me blabla. | |
CTCP_ALL | all CTCP commands received. | |
The following CTCP hook types are optionally compiled into the client | ||
CTCP_CLIENTINFO
CTCP_FINGER CTCP_HELP CTCP_PING CTCP_SED CTCP_SOURCE CTCP_TIME CTCP_USERINFO CTCP_VERSION |
CTCP_ALL "* CLIENTINFO "
CTCP_ALL "* FINGER " CTCP_ALL "* HELP *" CTCP_ALL "* PING" CTCP_ALL "* SED *" CTCP_ALL "* SOURCE" CTCP_ALL "* TIME" CTCP_ALL "* USERINFO" CTCP_ALL "* VERSION" |
|
CTCP_REPLY | received a CTCP reply. | |
CTC_UNKNOWN | received an unknown CTCP request | |
DCC_CHAT_REQUEST | received a request to chat | |
DCC_SEND_REQUEST | received a request to receive a file | |
ERROR | IRC server sent an error text | |
INVITE | got an invitation to join a channel | |
JOIN | somebody joined a channel | |
JUNK | IRC server sent junk (unknown stuff) | |
KICK | somebody got kicked off a channel | |
MODE | mode change (channel or person) | |
NICK | somebody changed nick | |
NOTE | received a note | |
PART | somebody left channel | |
PING | received an IRC server ping | |
PONG | received an IRC server pong | |
FLOOD_PRIVATE | current message triggered private flood alarm | |
PRIVATE_MSG | received a private message | |
PRIVATE_NOTICE | received a private notice | |
FLOOD_PUBLIC | this message triggered channel flood alarm | |
PUBLIC_MSG | got a channel message | |
PUBLIC_NOTICE | got a channel notice | |
QUIT | someone signed off IRC | |
RAW_IRC | got a line from an IRC server. | |
SILENCE | Undernet servers send silence messages (can't figure that out) | |
TOPIC | channel topic changed | |
SERVER_NOTICE | got a server notice. | |
WALLOPS | public address system; abused to extinction | |
N | Server numeric reply. N is a number, usually between 1 and 999 inclusive. For a list of some known numeric replies, see the file include/server-numerics.h. |
(gs-set! 'autorejoin #f)
(gs-on "#kick" 5000 "*" (lambda(m w)
(define server (gs-window-server2 w)); obtain the server object for the connection
(define kicked-person (next-word m 2))
(define our-nick (server:nick server))
(if(string-ci=? kicked-person our-nick);; if 'you' have been kicked
(begin (define channel (next-word m 3))
(define chankey (channel:key (gs-channel2 channel w))) ;; save channel key
(gs-new-alarm 20 (lambda(junk morejunk) ;; set a timer for 20 seconds
(gs-exec (string-append "JOIN " channel " " (if(not chankey) ""
chankey)) w)))))))
2. Ignore CTCP error messages from clients that do not understand encrypted messages SED.
(gs-preempt "CTCP_REPLY" "*ERRMSG *SED*")
3. Ignore all channel messages on Dalnet if you're marked away.
(gs-preempt "RAW_IRC" "*.dal.net,* * PRIVMSG [&#]* *" (lambda(m w)
(server:away? (gs-window-server2 w))))
4.Auto-op everyone from the .de TLD who join any channel on which you are channel op.
(gs-on "#join" 101 "* *.de *" (lambda(m w)
(define joined_channel (next-word m 2))
;; now op them if u are op
(if(channel:op? (gs-channel joined_channel w))
(gs-execute w "/mode " joined_channel " +o " ($nick m)))))
(define quit-message "See y'all in hell...\n") (gs-on "#EXIT" 999999 "*" (lambda(junk mooo) (define socket-list (servers-socket-fd)); get a list of all socket descriptors (if(not(null? socket-list)); at least 1 server connection exists (for-each (lambda(i) (gs-raw-write (car i) (string-append "QUIT :" quit-message "\n"))) socket-list))))2. Don't send anything to the server that contains the word "animalistic" (case insensitive) anywhere in it. The first word is the name and port of the server so we skip it.
(gs-iregex-hook "send_to_server" "^[^ ].* animalistic")*There*'s a problem with the last example. I cant work it out.
- Sula Primerix II - 0.09.2b (1Aug99) ------------------------------------- o New hook types 1. RAW_NUMERIC 2. RAW_NON_NUMERIC These are cheaper alternatives to RAW_IRC. RAW_IRC ::= RAW_NUMERIC + RAW_NON_NUMERIC. 3. IRC_LINE_OUT IRC_LINE_OUT is triggered when a line from IRC is about to be displayed on the screen. The line has already been processed (hook-checked, dissected, formatted, fired at, etc), and the user is about to see the final result. This hook type offers the possibility of setting hooks based on what the user *actually* sees from IRC. Example: How to grab HT URLs /on #irc_line_out 200 *http://?*.?* (grab-url "$2-") or, since we're only interested in incoming lines, (gs-add-hook "#irc_line_out" 200 "*http://?*.?*" (lambda(m w) (if(not(string=? "OUTGOING" (next-word m 1))) (grab-url m)))) 'grab-url' would be a procedure that can extract URLs from a string. Arguments to IRC_LINE_OUT: server,port str_type str_line int_flag As usual, server,port is the server to which you're connected. type describes the kind of message we're dealing with. It may take any of following values: "PRIV_MSG", "PUB_MSG", "PRIV_NOTICE", "PUB_NOTICE", "ACTION", "DCC", "CTCP_REPLY", "NOTE","SNOTICE", "QUIT", "TOPIC", "OUTGOING", "KICK", "WALLOP", "NUMERIC","MISC". "OUTGOING" is for messages that you are sending out. "MISC" is for all else not mentioned. str_line is the final text to be displayed; if flag==0, text is to be displayed in the upper window. Much information is hidden by the time a message reaches IRC_LINE_OUT (no sender name or address, no destination etc). This is because str_line may not be what somebody actually sent; it might have been altered along the way by other hooks or by a format string. For example, if the variable fmt_pub_msg got set to "<%n> crap: %p", channel messages would get the word "crap" added to them and it would be inaccurate to think that the author of message sent that. o changes to hook type DCC_SEND_START: one argument has been added: the position (offset) in the file from where sending is starting. - Sula Primerix II - 0.09.3 (6Aug99) ------------------------------------ Normally, hook handlers get only a copy of the server or client message to work with. All modifications to the message are local to the hook. It is now possible for two hook types to globally *modify* the IRC or client message such that subsequent hooks and the user never see the original text. - Hook type RAW_NON_NUMERIC Is allowed to modify messages by calling the procedure gs-modify-irc-output. Other hooks and the client then never see the original IRC message and cant tell whether it's been modified or not. For fear of abuse, support for this tampering must be built into the client using -DMUTABLE_IRC at compile time. Also, at runtime, 'mutable_irc' must be SET on. And since RAW_NON_NUMERIC doesnt work on server numerics, numeric replies cannot be modified. Hooks of type RAW_NON_NUMERIC always use the original message. See the scripts scripts/afuck.scm for an example. - Hook type INPUT Can modify user input by calling the procedure gs-modify-user-input with the replacement text as argument. Related flags (currenntly for RAW_NON_NUMEIRIC) + runtime: /set mutable_irc on|off|toggle + X resource entry: *.mutable_irc: on|of + command line option: -mutable_irc (implies true)EOF