-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathusers.lisp
127 lines (109 loc) · 5.14 KB
/
users.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
(in-package #:org.shirakumo.maiden.clients.irc)
;; FIXME: We need to lock up the user and channel tables //everywhere//
;; during updates to ensure consistency during changes. Otherwise
;; things like rapid nicks or parts/joins could end up confusing
;; the system.
(defclass irc-server (client-entity)
())
(defclass irc-user (simple-user)
((user :initarg :user :reader user)
(host :initarg :host :reader host))
(:default-initargs
:user ""
:host ""))
(defmethod reply ((user irc-user) message &rest args)
(irc:privmsg (client user) (name user) (apply #'format NIL message args)))
(defclass irc-channel (simple-channel)
((topic :initarg :topic :accessor topic))
(:default-initargs :topic ""))
(defmethod reply ((channel irc-channel) message &rest args)
(irc:privmsg (client channel) (name channel) (apply #'format NIL message args)))
(defmethod ensure-user ((name string) (client irc-client))
(or (find-user name client)
(make-instance 'irc-user :name name :client client)))
(defmethod ensure-channel ((name string) (client irc-client))
(or (find-channel name client)
(make-instance 'irc-channel :name name :client client)))
(defun prune-users (client)
(loop for user being the hash-values of (user-map client)
do (unless (channels user)
(remove-user user client))))
(defun coerce-irc-object (name user host client)
(let ((name (string-left-trim "~=+*@" name)))
(cond ((find #\. name)
(make-instance 'irc-server :name name :client client))
((find #\# name)
(or (find-channel name client)
(make-instance 'irc-channel :name name :client client)))
(T
(or (find-user name client)
(make-instance 'irc-user :name name
:user (or user "")
:host (or host "")
:client client))))))
(define-handler (irc-client track-join irc:msg-join) (client ev channel user)
:match-consumer 'client
(cond ((matches user (nickname client))
(setf (gethash (name channel) (channel-map client)) channel))
(T
;; Note!! We have to be 100% fukin' sure that this is either the ONLY instance
;; of this particular user, or one that already exists in the map.
;; Otherwise we might run into inconsistency issues where some parts of
;; the system might have a dupe of a user with a different object
;; identity! This can actually happen no matter what if we are particularly
;; unlucky about concurrency and other parts of the system retaining user
;; objects for a prolonged period of time.
(setf (find-channel (name channel) user) channel)
(setf (find-user (name user) client) user)
(setf (find-user (name user) channel) user))))
(define-handler (irc-client track-namreply irc:rpl-namreply) (client ev channel info)
:match-consumer 'client
(dolist (user (cl-ppcre:split " +" info))
(let ((object (coerce-irc-object user NIL NIL client)))
(setf (find-user (name object) channel) object)
(when (typep object 'irc-user)
(setf (find-channel (name channel) object) channel))
;; We somehow missed a JOIN, but we trust NAMREPLY more.
(unless (find-user object client)
(setf (find-user (name object) client) object)))))
(define-handler (irc-client track-nick irc:msg-nick) (client ev user nickname)
:match-consumer 'client
:after '(nick-change)
(let ((old-nick (name user)))
(unless (matches nickname (nickname client))
(dolist (channel (channels user))
(remove-user (name user) channel)
(setf (find-user nickname channel) user))
(when (find-user user client)
(remove-user (name user) client)
(setf (find-user nickname client) user))
(setf (name user) nickname))
;; Issue nick change event
(do-issue (core ev) user-name-changed :client client :user user :old-name old-nick)))
(define-handler (irc-client track-leave irc:msg-part) (client ev channel user)
:match-consumer 'client
(cond ((matches user (nickname client))
(remove-channel channel client))
(T
(remove-channel channel user)
(remove-user (name user) channel)))
(prune-users client))
(define-handler (irc-client track-kick irc:msg-kick) (client ev channel nickname)
:match-consumer 'client
(cond ((matches nickname (nickname client))
(remove-channel channel client))
(T
(remove-channel channel (ensure-user nickname channel))
(remove-user nickname channel)))
(prune-users client))
(define-handler (irc-client track-quit irc:msg-quit) (client ev user)
:match-consumer 'client
(remove-user user client))
(define-handler (irc-client track-kill irc:msg-kill) (client ev nickname)
:match-consumer 'client
(let ((user (find-user nickname client)))
(when user (remove-user user client))))
(define-handler (irc-client track-topic irc:rpl-topic) (client ev channel topic)
:match-consumer 'client
(let ((channel (ensure-channel channel client)))
(setf (topic channel) topic)))