-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathconfig.pl
More file actions
323 lines (250 loc) · 9.02 KB
/
config.pl
File metadata and controls
323 lines (250 loc) · 9.02 KB
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
:- module(config, [
host/1,
port/1,
nick/1,
pass/1,
chans/1,
bot_hostname/1,
bot_servername/1,
bot_realname/1,
desired_extensions/1,
extensions/2,
sync_extensions/2,
set_extensions/1,
time_limit/1,
init_extensions/0,
goals_to_concurrent/2,
add_new_extensions/1,
remove_extensions/1,
load_new_extensions/1,
is_script/1,
valid_extensions/1,
check_valid_extensions/1
]).
:- use_module(library(irc_client)).
:- use_module(library(settings)).
:- use_module(library(dcg/basics)).
:- use_module(library(func)).
:- use_module(library(lambda)).
:- dynamic extensions/2.
:- dynamic sync_extensions/2.
%--------------------------------------------------------------------------------%
% Connection Constants
%--------------------------------------------------------------------------------%
% DO NOT modify this file to set up a bot. When you
% check in code it'll publish your settings, including your
% password, and it'll always be a merge problem, and you'll have
% to set each time this file changes.
%
% Instead, alter the
% settings by querying set_setting/2 followed by save_settings/0. e.g.
% to change the port do
%
% ?- set_setting(config:port, 9999), save_settings.
%
% a file settings.db will appear in your working
% directory.
%
% DO NOT check in your
% settings.db file
%
% SEE THE BOTTOM OF THIS FILE FOR SOME EXTRA DOCUMENTATION WITH RESPECT TO A FEW
% OF THESE SETTINGS
% Constants for general server connection specs
:- setting(host, atom, 'chat.freenode.net', 'IRC host to connect to').
:- setting(port, between(1,0x7FFF), 6667, 'Port to connect to').
:- setting(nick, text, yesbot, 'Bots nick (name) on IRC').
:- setting(pass, text, notpassword, 'Bots password.').
:- setting(chans, list(text), [ '##prolog' ],
'List of channels to connect to').
:- setting(extensions, list(atom),
[bot_control, output, link_shortener,
prolog_eval, yesbot_version,
isup, dict, swi_object_search, wiki_search, references, help,
emoticons],
'list of extensions to load').
% Constants for user registration specs
:- setting(bot_hostname, atom, hostname,
'The user''s hostname (usually ignored).').
:- setting(bot_servername, atom, servername,
'The user''s servername (usually ignored).').
:- setting(realname, atom, anonymous, 'Bot owner''s real name').
% Time interval for checking pings (connectivity safeguard)
:- setting(ping_interval, positive_integer, 300,
'Time interval (in seconds) to check for pings and handle reconnection.').
:- initialization load_settings('settings.db').
host(Host) :-
setting(host, Host).
port(Port) :-
setting(port, Port).
nick(Nick) :-
setting(nick, Nick).
pass(Pass) :-
setting(pass, Pass).
chans(Chans) :-
setting(chans, Chans).
bot_hostname(Name) :-
setting(bot_hostname, BotHostName),
( BotHostName = use_host
-> setting(host, Name)
; Name = BotHostName
).
bot_servername(Name) :-
setting(bot_servername, BotServerName),
( BotServerName = use_host
-> setting(host, Name)
; Name = BotServerName
).
bot_realname(Name) :-
setting(realname, Name).
desired_extensions(List) :-
setting(extensions, List).
time_limit(Limit) :-
setting(ping_interval, Limit).
%--------------------------------------------------------------------------------%
% Extensions/Hot loading
%--------------------------------------------------------------------------------%
%% init_extensions is semidet.
%
% Initialized user chosen extensions and assert handlers at top level.
init_extensions :-
Import_extension_module = (\Extension^use_module(extensions/Extension)),
Qualify = (\X^X^Q^(Q = X:X)),
desired_extensions(Extensions),
partition(is_sync, Extensions, Sync, Async),
length(Sync, N0),
length(Async, N1),
maplist(retractall, [sync_extensions(_,_),extensions(_,_)]),
asserta(sync_extensions(Sync, N0)),
asserta(extensions(Async, N1)),
maplist(Import_extension_module, Extensions),
maplist(Qualify, Sync, Sync, SyncHandlers),
maplist(Qualify, Async, Async, AsyncHandlers),
append(AsyncHandlers, [goals_to_concurrent(SyncHandlers)], Handlers),
assert_handlers(irc, Handlers).
%% goals_to_concurrent(+Goals, +Msg) is det.
%
% Concurrently calls a list of Goals with Msg, the current server message.
% Until the last goal has been completed, this predicate will block. This is
% used for extensions that _must_ process messages in order.
goals_to_concurrent(Goals, Msg) :-
sync_extensions(_, N),
( N > 0
-> goals_to_calls(Goals, Calls),
maplist(call_with_msg(Msg), Calls, RunCalls),
concurrent(N, RunCalls, [])
; true
).
call_with_msg(Msg, Call, call(Call, Msg)).
goals_to_calls(Goals, Calls) :-
maplist(goal_to_call, Goals, Calls).
goal_to_call(Goal, Call) :-
Call = (\Msg^call(Goal,Msg)).
%% set_extensions(:Extensions) is det.
%
% Set the extensions to be loaded on startup. (Sanity checked)
set_extensions(Extensions) :-
check_valid_extensions(Extensions),
set_setting(config:extensions, Extensions).
%% add_new_extensions(+New) is semidet.
%
% Adds new extensions on top of whatever extensions are currently loaded in the
% the bot at the moment. These settings will not persist on restart; persisting
% these settings must be done by preceding a save_settings/0 call with this call.
add_new_extensions(New) :-
setting(config:extensions, Es),
append(New, Es, Extensions),
load_new_extensions(Extensions).
%% remove_extensions(+Es) is semidet.
%
% Subtract the requested list of extensions Es, from the currently loaded
% extensions during runtime.
remove_extensions(Es) :-
setting(config:extensions, Current),
subtract(Current, Es, Extensions),
load_new_extensions(Extensions).
%% load_new_extensions(+Es) is semidet.
%
% Load a new set of extensions and initalize them into the current bot session.
% This will not save these settings on restart. To make them persistent, this
% predicate call must precede a call to save_settings/0.
load_new_extensions(Es) :-
check_valid_extensions(Es),
set_setting(config:extensions, Es),
retractall(info:extensions(_,_)),
retractall(info:sync_extensions(_,_)),
init_extensions.
%%%%%%%%%%%%%%%%%
% Sanity Checks %
%%%%%%%%%%%%%%%%%
%% is_sync(+Name) is semidet.
%
% True if the extension name is prefixed with 'sync_'. (synchronous)
is_sync(Name) :-
is_sync_(atom_codes $ Name, _Rest).
is_sync_ --> `sync_`.
%% script_extension(+File, Without) is semidet.
%
% True if File ends in `.pl` and Without is an atom devoid of this ending.
script_extension(File, Without) :-
string_without(`.`, WO, atom_codes $ File, `.pl`),
atom_codes(Without, WO).
%% is_script(+File) is semidet.
%
% True if File is a prolog script file (ending in `.pl`).
is_script(File) :-
script_extension(File, _).
%% check_valid_extensions(Es) is det.
%
% True iff Es is a valid subset of extensions. An existence error will be thrown
% otherwise.
check_valid_extensions(Es) :-
( valid_extensions(Es)
-> true
; existence_error(invalid_subset, Es)
).
%% valid_extensions(+Extensions) is semidet.
%
% True if Extensions is a subset of extensions available to yesbot.
valid_extensions(Extensions) :-
% Get all files from extensions directory
directory_files(extensions, Files),
% Filter all files by pl scripts
include(is_script, Files, A),
% Transform all scripts into extension names
maplist(script_extension, A, B),
subset(Extensions, B).
%--------------------------------------------------------------------------------%
% Information from IRC RFC
%--------------------------------------------------------------------------------%
/*
For those of you whom are curious about the IRC user registration process:
These are relevant excerpts from Section 4.1.3 of the IRC RFC.
(This document is available at: https://tools.ietf.org/html/rfc1459)
Command: USER
Parameters: <username> <hostname> <servername> <realname>
The USER message is used at the beginning of connection to specify
the username, hostname, servername and realname of a new user. It is
also used in communication between servers to indicate new user
arriving on IRC, since only after both USER and NICK have been
received from a client does a user become registered.
Between servers USER must to be prefixed with client's NICKname.
Note that hostname and servername are normally ignored by the IRC
server when the USER command comes from a directly connected client
(for security reasons), but they are used in server to server
communication. This means that a NICK must always be sent to a
remote server when a new user is being introduced to the rest of the
network before the accompanying USER is sent.
It must be noted that realname parameter must be the last parameter,
because it may contain space characters and must be prefixed with a
colon (':') to make sure this is recognised as such.
Since it is easy for a client to lie about its username by relying
solely on the USER message, the use of an "Identity Server" is
recommended. If the host which a user connects from has such a
server enabled the username is set to that as in the reply from the
"Identity Server".
Numeric Replies:
ERR_NEEDMOREPARAMS ERR_ALREADYREGISTRED
Examples:
USER guest tolmoon tolsun :Ronnie Reagan
*/