2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module hitwit
is aliced
;
32 // ////////////////////////////////////////////////////////////////////////// //
33 public abstract class Highlighter
{
36 bool isOurFolder (Folder
fld) nothrow {
37 import std
.path
: globMatch
;
38 if (fld is null) return false;
39 return globMatch(fld.folderPath
, foldermask
);
42 bool hiCheck (Folder
fld, Article art
, uint aidx
, ArticleBase abase
) nothrow; // no need to check folder here, it is passed just for informational purposes
43 string
twitCheck (Folder
fld, Article art
, uint aidx
, ArticleBase abase
) nothrow; // no need to check folder here, it is passed just for informational purposes
47 final class HighlightFrom
: Highlighter
{
49 bool anchorStart
, anchorEnd
;
51 override bool hiCheck (Folder
fld, Article art
, uint aidx
, ArticleBase abase
) nothrow {
52 if (art
is null) return false;
54 string cs
= art
.frommail
;
55 if (pat
.length
== 0) return false;
57 if (anchorEnd
&& cs
.length
!= pat
.length
) return false;
58 if (cs
.startsWithCI(pat
)) return true;
59 } else if (anchorEnd
) {
61 if (cs
.length
< pat
.length
) return false;
62 if (cs
[$-pat
.length
..$].strEquCI(pat
)) return true;
66 while (cs
.length
>= pat
.length
) {
67 if (cs
.startsWithCI(pat
)) return true;
74 override string
twitCheck (Folder
fld, Article art
, uint aidx
, ArticleBase abase
) nothrow { return null; }
78 final class TwitThread
: Highlighter
{
81 override bool hiCheck (Folder
fld, Article art
, uint aidx
, ArticleBase abase
) nothrow { return false; }
83 override string
twitCheck (Folder
fld, Article art
, uint aidx
, ArticleBase abase
) nothrow {
84 if (msgid
.length
== 0) return null;
85 while (art
!is null) {
86 if (art
.msgid
== msgid
) return "";
96 final class TwitNick
: Highlighter
{
103 private final string
checkArt (Article art
) nothrow @nogc {
104 if (art
is null) return null;
105 if (name
.length
== 0 && mail
.length
== 0) return null;
106 if (name
.length
&& art
.fromname
!= name
) return null;
107 if (mail
.length
&& !art
.frommail
.strEquCI(mail
)) return null;
108 return (title
!is null ? title
: "");
111 override bool hiCheck (Folder
fld, Article art
, uint aidx
, ArticleBase abase
) nothrow { return false; }
113 override string
twitCheck (Folder
fld, Article art
, uint aidx
, ArticleBase abase
) nothrow {
114 auto res
= checkArt(art
);
115 while (res
is null && art
!is null) {
117 if (aidx
== 0) break;
125 private __gshared Highlighter
[] hitwlist
;
126 public __gshared
uint hitwlistUpdateCounter
;
129 // ////////////////////////////////////////////////////////////////////////// //
130 public bool forEachHitTwit (scope bool delegate (Highlighter ht
) nothrow dg
) nothrow {
131 if (dg
is null) return false;
132 foreach (Highlighter hg
; hitwlist
) {
133 if (dg(hg
)) return true;
139 // ////////////////////////////////////////////////////////////////////////// //
140 // cannot use module ctor due to cyclic dependencies
141 public void hitwitInitConsole () {
142 //highlight fromemail ^ketmar@ketmar.no-ip.org$ folder_mask dmars_ng/*
143 conRegFunc
!((ConString
[] args
) {
144 auto origargs
= args
;
145 auto hf
= new HighlightFrom();
146 while (args
.length
) {
147 if (args
.length
< 2) { conwriteln("highlight: invalid args: ", origargs
); return; }
154 if (hf
.email
.length
!= 0) {
155 conwriteln("highlight: duplicate email option: '", arg
, "'");
156 conwriteln("highlight: invalid args: ", origargs
);
163 if (hf
.foldermask
.length
!= 0) {
164 conwriteln("highlight: duplicate folder mask option: '", arg
, "'");
165 conwriteln("highlight: invalid args: ", origargs
);
168 hf
.foldermask
= arg
.idup
;
171 conwriteln("highlight: unknown options: '", opt
, "'");
172 conwriteln("highlight: invalid args: ", origargs
);
176 if (hf
.email
.length
&& hf
.email
[0] == '^') { hf
.email
= hf
.email
[1..$]; hf
.anchorStart
= true; }
177 if (hf
.email
.length
&& hf
.email
[$-1] == '$') { hf
.email
= hf
.email
[0..$-1]; hf
.anchorEnd
= true; }
178 if (hf
.email
.length
== 0) { conwriteln("highlight: invalid args (no email): ", origargs
); return; }
179 if (hf
.foldermask
.length
== 0) { conwriteln("highlight: invalid args (no folder mask): ", origargs
); return; }
181 // HACK! this is to avoid false positives in GC
182 //{ import core.memory : GC; GC.setAttr(hitwlist.ptr, GC.BlkAttr.NO_SCAN|GC.BlkAttr.NO_INTERIOR); }
183 //conwriteln("new highlihgter: ", origargs);
184 ++hitwlistUpdateCounter
;
186 })("highlight", "highight messages in thread pane");
188 //twit_thread folder_mask dmars_ng/* msgid <nsp82c$t2g$1@digitalmars.com>
189 conRegFunc
!((ConString
[] args
) {
190 auto origargs
= args
;
191 auto hf
= new TwitThread();
192 while (args
.length
) {
193 if (args
.length
< 2) { conwriteln("twit: invalid args: ", origargs
); return; }
200 if (hf
.foldermask
.length
!= 0) {
201 conwriteln("twit: duplicate folder mask option: '", arg
, "'");
202 conwriteln("twit: invalid args: ", origargs
);
205 hf
.foldermask
= arg
.idup
;
211 if (hf
.msgid
.length
!= 0) {
212 conwriteln("twit: duplicate message id option: '", arg
, "'");
213 conwriteln("twit: invalid args: ", origargs
);
219 conwriteln("twit: unknown options: '", opt
, "'");
220 conwriteln("twit: invalid args: ", origargs
);
224 if (hf
.msgid
.length
== 0) { conwriteln("twit: invalid args (no msgid): ", origargs
); return; }
225 if (hf
.foldermask
.length
== 0) { conwriteln("twit: invalid args (no folder mask): ", origargs
); return; }
227 // HACK! this is to avoid false positives in GC
228 //{ import core.memory : GC; GC.setAttr(hitwlist.ptr, GC.BlkAttr.NO_SCAN|GC.BlkAttr.NO_INTERIOR); }
229 //conwriteln("new highlihgter: ", origargs);
230 ++hitwlistUpdateCounter
;
232 })("twit_thread", "twit threads");
241 conRegFunc
!((ConString
[] args
) {
242 auto origargs
= args
;
243 auto hf
= new TwitNick();
244 while (args
.length
) {
245 if (args
.length
< 2) { conwriteln("twit_set: invalid args: ", origargs
); return; }
252 if (hf
.foldermask
.length
!= 0) {
253 conwriteln("twit_set: duplicate folder mask option: '", arg
, "'");
254 conwriteln("twit_set: invalid args: ", origargs
);
257 hf
.foldermask
= arg
.idup
;
263 if (hf
.msgid
.length
!= 0) {
264 conwriteln("twit_set: duplicate message id option: '", arg
, "'");
265 conwriteln("twit_set: invalid args: ", origargs
);
271 if (hf
.title
!is null) {
272 conwriteln("twit_set: duplicate title option: '", arg
, "'");
273 conwriteln("twit_set: invalid args: ", origargs
);
276 hf
.title
= (arg
.length ? arg
.idup
: "");
279 if (hf
.name
.length
!= 0) {
280 conwriteln("twit_set: duplicate name option: '", arg
, "'");
281 conwriteln("twit_set: invalid args: ", origargs
);
289 if (hf
.mail
.length
!= 0) {
290 conwriteln("twit_set: duplicate mail option: '", arg
, "'");
291 conwriteln("twit_set: invalid args: ", origargs
);
298 if (hf
.url
.length
!= 0) {
299 conwriteln("twit_set: duplicate url option: '", arg
, "'");
300 conwriteln("twit_set: invalid args: ", origargs
);
306 conwriteln("twit_set: unknown options: '", opt
, "'");
307 conwriteln("twit_set: invalid args: ", origargs
);
311 if (hf
.name
.length
== 0 && hf
.mail
.length
== 0) { conwriteln("twit_set: invalid args (name/mail): ", origargs
); return; }
312 if (hf
.foldermask
.length
== 0) { conwriteln("twit_set: invalid args (no folder mask): ", origargs
); return; }
313 if (hf
.title
is null) hf
.title
= "";
315 // HACK! this is to avoid false positives in GC
316 //{ import core.memory : GC; GC.setAttr(hitwlist.ptr, GC.BlkAttr.NO_SCAN|GC.BlkAttr.NO_INTERIOR); }
317 //conwriteln("new highlihgter: ", origargs);
318 ++hitwlistUpdateCounter
;
320 })("twit_set", "twit nicks");
326 conRegFunc
!((ConString
[] args
) {
327 auto origargs
= args
;
328 string fmask
, name
, mail
;
329 while (args
.length
) {
330 if (args
.length
< 2) { conwriteln("twit_unset: invalid args: ", origargs
); return; }
337 if (fmask
.length
!= 0) {
338 conwriteln("twit_unset: duplicate folder mask option: '", arg
, "'");
339 conwriteln("twit_unset: invalid args: ", origargs
);
345 if (name
.length
!= 0) {
346 conwriteln("twit_unset: duplicate name option: '", arg
, "'");
347 conwriteln("twit_unset: invalid args: ", origargs
);
355 if (mail
.length
!= 0) {
356 conwriteln("twit_unset: duplicate mail option: '", arg
, "'");
357 conwriteln("twit_unset: invalid args: ", origargs
);
363 conwriteln("twit_unset: unknown options: '", opt
, "'");
364 conwriteln("twit_unset: invalid args: ", origargs
);
368 if (name
.length
== 0 && mail
.length
== 0) { conwriteln("twit_unset: invalid args (name/mail): ", origargs
); return; }
369 //if (fmask.length == 0) { conwriteln("twit_unset: invalid args (no folder mask): ", origargs); return; }
370 for (int hidx
= 0; hidx
< hitwlist
.length
; ++hidx
) {
371 if (auto twl
= cast(TwitNick
)hitwlist
[hidx
]) {
372 if (fmask
.length
&& twl
.foldermask
!= fmask
) continue;
373 if (twl
.name
!= name || twl
.mail
!= mail
) continue;
374 foreach (immutable cc
; hidx
+1..hitwlist
.length
) hitwlist
.ptr
[cc
-1] = hitwlist
.ptr
[cc
];
375 hitwlist
[$-1] = null;
376 hitwlist
.length
-= 1;
377 hitwlist
.assumeSafeAppend
;
378 // HACK! this is to avoid false positives in GC
379 //{ import core.memory : GC; GC.setAttr(hitwlist.ptr, GC.BlkAttr.NO_SCAN|GC.BlkAttr.NO_INTERIOR); }
383 ++hitwlistUpdateCounter
;
385 })("twit_unset", "untwit nicks");
389 // ////////////////////////////////////////////////////////////////////////// //
390 public void twitThread (string folderPath
, string msgid
) {
392 concmdfex
!"twit_thread folder_mask \"%s\" msgid \"%s\""((ConString cc
) { twcmd
= cc
.idup
; }, folderPath
, msgid
);
394 import std
.path
: buildPath
;
395 auto fo
= VFile(buildPath(mailRootDir
, "auto_twit_threads.rc"), "a");
396 fo
.writeln(twcmd
.xstrip
);
397 } catch (Exception e
) {
398 conwriteln("ERROR saving thread twit: ", e
.msg
);