flnews_post_proc 1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,336 @@
1
+ =======================
2
+ flnews_post_proc
3
+ =======================
4
+ ------------------------------------------
5
+ Post-Processor for the flnews newsreader
6
+ ------------------------------------------
7
+
8
+ SYNOPSIS
9
+ =======================
10
+
11
+ An article is piped-in to the post-processor. This is normally done
12
+ automatically as soon as the variable “post_proc“ in the flnews configuration
13
+ file is set to the flnews_post_proc.
14
+
15
+ Locally stored articles can be piped in to the post-processor, as needed
16
+ for testing purposes, with a command-line like the following:
17
+
18
+ **flnews_post_proc < article**
19
+
20
+ DESCRIPTION
21
+ =======================
22
+ The flnews newsreader is sufficient for Usenet access, i.e. to receive and read
23
+ articles from -, as well as to write and post articles to newsgroups.
24
+
25
+ When you compare news-clients, you will always notice the differences and
26
+ choose the software that you prefer. Flnews however, has the charm that you can
27
+ influence how the program itself works but also modify posts that flnews
28
+ produces, just before the program will transmit them to the chosen nntp-server.
29
+
30
+ The flnews_post_proc can add and change details of a post, in ways that are
31
+ currently not possible with flnews alone. As the program is configurable, it
32
+ can probably respond to the needs of some Usenet users. You should, however,
33
+ rather take it as an example for what can be done and an inspiration for your
34
+ own creations.
35
+
36
+ The limits of a basic newsreader — what the program can do
37
+ ----------------------------------------------------------
38
+ While the articles that flnews creates, are complete and ready to be posted,
39
+ some users may not always agree with the result and for arbitrary reasons:
40
+
41
+ * There may be inconveniences when you post to different newsgroups in
42
+ different languages, as an introductory line which refers to a previous
43
+ post can only be set once in the flnews-configuration. The consequence
44
+ can be that your post to a french newsgroup begins with an introduction
45
+ in English.
46
+ My post-processor program can set an introductory line specifically
47
+ chosen for one or several newsgroups.
48
+
49
+ * The same conflict arises, when you have set a standard signature-text and
50
+ would like to replace it against another, based on the newsgroup you are
51
+ about to post to.
52
+ The post-processor program sets specific signatures as configured for one
53
+ or several newsgroups. You can even specify that a signature should be
54
+ picked randomly from a file, containing several signatures.
55
+
56
+ * Some custom headers may serve to convey additional information to
57
+ interested readers of your post, like GnuPG key IDs, your language skills
58
+ or the like. The signature may be a better choice than custom headers.
59
+ You are free. I just mention face and x-face but prefer that you do not
60
+ remember I did.
61
+ Custom-headers may be defined in the configuration file for the program
62
+ and will then be added to each outgoing post.
63
+
64
+ * The Archive- and the X-No-Archive header are sometimes set to avoid that an
65
+ article be saved and stays available to search-engines (Google, notably).
66
+ Test- postings, for example, do probably not justify at all that they would
67
+ be referenced in search-results.
68
+ The post-processor program can impose both headers for all posts to certain
69
+ newsgroups.
70
+
71
+ **ATTN** As of 2024, the header “X-No-Archive“ has lost most of its utility
72
+ and it is the decision of server operators to honor it or not.
73
+
74
+ * If a news post contains many references to either other posts or Web
75
+ pages, the text can be cluttered with URLs.
76
+ The post-processor can identify marked text fragments and transform them
77
+ into footnotes, which will be attached as a list at the bottom of the
78
+ post.
79
+ This works almost like the <ref/> link in Wikipedia, but the delimiter
80
+ can be determined in the configuration file.
81
+ Example (with %=):
82
+ This is an object %=and this becomes the footnote, describing the object
83
+ further=%
84
+
85
+ Dialog to override settings
86
+ ---------------------------
87
+ You can have a dialog displayed just before the post-processor is invoked, to
88
+ **disable** some configuration options. Provided that either YAD, Zenity,
89
+ Whiptail or only xterm are available on your computer, you can choose from
90
+ the following options. You **cannot** use the dialog to enable options which
91
+ have not yet been set in the configuration.
92
+
93
+ * Signatures, as set in the configuration **can be ignored**. Either
94
+ a default signature will appear as set in flnews or none.
95
+ * Custom Headers, if configured, can be **omitted**.
96
+ * The Archive- and X-No-Archive headers, if set for the current newsgroup,
97
+ **can be ignored**.
98
+ * Logging can be **switched off**, if set.
99
+
100
+ Pushing Esc or the cancle-button of the dialog interrupts the process, flnews
101
+ will not post the article.
102
+
103
+ You can disable the dialog, which ensures that all configured options will be
104
+ applied, without the need for further interaction (see below, option
105
+ OVERRIDE_CONFIG).
106
+
107
+ CONFIGURATION
108
+ ===============
109
+ On first execution of the program, a copy of the original configuration file is
110
+ created in */home/[user]/.flnews_post_proc.conf*
111
+ It is this file which is used from then on. If you delete it, it will be
112
+ recreated, on the next occasion, but your own changes will be lost.
113
+
114
+ The configuration file is in YAML syntax and full of explanations. The
115
+ variables defined in this file can be classified as belonging to one of two
116
+ categories:
117
+
118
+ * Variables describing values originally set by flnews, which should be used or
119
+ replaced. The important elements are usually matched in a capture group.
120
+
121
+ * Variables defining the new or altered content.
122
+
123
+ **FUP_NAME**
124
+ A Regular Expression, describing the string which contains the name of
125
+ previous poster who is the author of a quoted post. This string is
126
+ recognized in the original article and may be used with the fitting element
127
+ from *GROUP_INTRO*, below. The Regexp-format is that of the Regexp class in
128
+ Ruby, noted as a String. Beware to mask a backslash '\\' by another one,
129
+ like in the example. A capture-group '()' serves to extract the name from the
130
+ match result.
131
+
132
+ Leave this field empty to keep the default from the FLNews configuration
133
+ intact.
134
+
135
+ CONTENT: A String equivalent of a regular expression.
136
+
137
+ DEFAULT: EMPTY
138
+
139
+ EXAMPLE1: "On \\\\d+.\\\\d+.\\\\d{2,4} at \\\\d+:\\\\d+ **(.*)** wrote:"
140
+
141
+ EXAMPLE2: "**(.*)** wrote:"
142
+
143
+
144
+ **FUP_GROUP**
145
+ A Regular Expression, describing the string which contains the newsgroup
146
+ where the previous post, that you are referring to in the followup, had been
147
+ published.
148
+
149
+ Leave this field empty to ignore the precise group.
150
+
151
+ CONTENT: A String equivalent of a regular expression.
152
+
153
+ DEFAULT: EMPTY
154
+
155
+ EXAMPLE: "wrote in **(.*)**:"
156
+
157
+ **GROUP_INTROS**
158
+ Introductory strings, referring to the previous poster who is the author of a
159
+ quoted post. If you match the newsgroup of the post (see FUP_GROUP), you can
160
+ use these variables in the result.
161
+ Currently only %fup_name% and %fup_group% are reproduced in the resulting
162
+ introductory string.
163
+
164
+ CONTENT: A newsgroup or regexp per line, followed by a colon, a space and a String
165
+
166
+ DEFAULT: As configured in FLNews
167
+
168
+ EXAMPLE: alt.test: "Thus spoke %fup_name% on that baleful %fup_date%:"
169
+
170
+ **GROUP_SIGS**
171
+ A signature line per Newsgroup or a file path. The file should contain signatures,
172
+ already formatted and separated by 1 empty line. The program will randomly pick
173
+ one signature from the list.
174
+
175
+ ATTN! In multi line signatures, you have to use \\r\\n for line breaks.
176
+
177
+ CONTENT: A newsgroup or regexp per line, followed by a colon, a space and a String.
178
+
179
+ DEFAULT: As configured in flnews
180
+
181
+ EXAMPLE: alt.test: "Signature for alt.test\\r\\nsecond line"
182
+
183
+ EXAMPLE: comp.*: /home/[user]/.my_sigs
184
+
185
+ **CUSTOM_HEADERS**
186
+ Additional headers for the outgoing article
187
+
188
+ CONTENT: 1 line per header : a dash and space, then a String, comprising the
189
+ name of the header, ending in a colon and the value of the header.
190
+
191
+ DEFAULT: undefined
192
+
193
+ | EXAMPLE (2 headers):
194
+ | - 'X-My-Header: nothing fancy'
195
+ | - 'X-Another-Header: care not!'
196
+
197
+ **NO_ARCHIVE_GROUPS**
198
+ The newsgroups, where the headers “Archive: no” and “X-No-Archive: YES” shall
199
+ be set.
200
+
201
+ CONTENT: a dash and space, then a String, containing the name of the group or a regexp.
202
+
203
+ DEFAULT: empty
204
+
205
+ | EXAMPLE (1 group, 1 hierarchy):
206
+ | - "alt.test"
207
+ | - "^news.*"
208
+
209
+
210
+ **DEBUG_LOG**
211
+ The name of a file, where debug messages are written. Setting this
212
+ variable will enable the log. Leave empty to disable logging.
213
+
214
+ CONTENT: The name of a writable file, which will be created if inexistent
215
+ and overwritten if need be.
216
+
217
+ DEFAULT: empty
218
+
219
+ EXAMPLE: '/tmp/a_log-file.txt'
220
+
221
+ **LOG LEVEL**
222
+ One of debug, fatal, error, info, warn
223
+
224
+ **REFERENCES_SEPARATOR**
225
+ A symbol or sequence of symbols marking the end of the message-body and the
226
+ beginning of a list of “references” or “footnotes”. It will only appear, if
227
+ the original message-body contains text marked for use as such a footnote.
228
+ See *REFERENCES_DELIMITER*.
229
+
230
+ If the option is not defined or empty, the list of footnotes will appear
231
+ below the last line of the message body and no separator will be inserted.
232
+
233
+ CONTENT: A quoted symbol or sequence of symbols.
234
+
235
+ DEFAULT: empty
236
+
237
+ EXAMPLE: '---------'
238
+
239
+ **REFERENCES_DELIMITER**
240
+ A sequence of at least two symbols marking the beginning of a text which will
241
+ serve as footnote (or reference). The **reversed sequence** musst be used to
242
+ mark the end of the text. The presence of this sequence or symbol in the
243
+ original message body will cause the enclosed text to be moved below the
244
+ message body. The *REFERENCES_SEPARATOR*, if defined, will separate the
245
+ message from the list of footnotes.
246
+ If this option is not defined or empty, footnotes are not created.
247
+
248
+ CONTENT a quoted symbol or sequence of symbols.
249
+
250
+ DEFAULT: none/empty
251
+
252
+ EXAMPLE: '%?'
253
+
254
+ **REFERENCE_FORMAT**
255
+ A format-string, using %s for a number, replacing the reference-
256
+ text in the message body.
257
+
258
+ DEFAULT: " %s)" -> becomes 1) ... 2) ... 3)
259
+
260
+ EXAMPLE: "(%s)" -> becomes (1) ... (2) ... (3)
261
+
262
+ **VFY_URLS**
263
+ A Boolean constant. It determines if the program shall verify and possibly
264
+ try to correct URLs. Even if URLs are identified as such, only a few
265
+ manipulations are attempted :
266
+ * Angular brackets '<' and '>' are added, if missing
267
+ * Slashes are added, if they are found missing after "http(s):"
268
+
269
+ If the variable is not set, a value 'yes' is assumed.
270
+
271
+ CONTENT: One of YES, yes, NO, no, and other variations of case.
272
+
273
+ DEFAULT: yes
274
+
275
+ Example: ... I let you guess.
276
+
277
+ **OVERRIDE_CONFIG**
278
+ A Boolean constant. You can choose to override the following configuration
279
+ options before an article is posted: GROUP_SIGS, XNAY_GROUPS, CUSTOM_HEADERS,
280
+ DEBUG_LOG and VFY_URLS. A dialog may be displayed which allows you to disable
281
+ any of these five options, so that the defaults from flnews prevail.
282
+
283
+ ATTN! Canceling the dialog or pushing the Esc-key does interrupt the process.
284
+ Flnews will not post the article.
285
+
286
+ Set this option to no, NO or similar to disable the dialog.
287
+
288
+ DEFAULT: yes
289
+
290
+ EXAMPLE: No
291
+
292
+ Other Information
293
+ =================
294
+
295
+ Testing
296
+ -------
297
+ The effects that the execution of the program will have on a posting can be
298
+ verified in two ways:
299
+
300
+ 1. By piping-in a post that had previously been saved to a file:
301
+
302
+ **:~$ /usr/local/bin/[post-processor] < [test-article]**
303
+
304
+ This will show the resulting new version of the article on screen, but you
305
+ can also pipe the output into another file. This is a great way to test a
306
+ program during development or to test your own configuration of the program.
307
+ 2. By posting directly into a test-newsgroup (like alt.test or similar). This
308
+ is mandatory before you really post to thematic newsgroups and when the
309
+ settings of the post-processor will affect the article.
310
+
311
+
312
+ Source-Code
313
+ -----------
314
+ The gem-file that you get with the gem-utility or from rubygems.org contains
315
+ all the code of the program and some documentation (this page notably). To read
316
+ its content, you must
317
+
318
+ 1. untar the gem-file with tar -xf flnews_post_proc-0.1.gem
319
+ 2. uncompress the data.gz archive: gunzip data.gz
320
+ 3. untar the resultig data.tar archive: tar -xf data.tar
321
+
322
+ This creates the directories bin, doc and lib.
323
+
324
+ License
325
+ -------
326
+ flnews_post_proc is distributed under the conditions of the WTFPL-2.0 or later
327
+ License (see http://www.wtfpl.net/txt/copying/ or license-text in the doc
328
+ directory of the gem-file).
329
+
330
+ Author
331
+ ------
332
+ | flnews_post_proc has been developed by
333
+ | Michael Uplawski <michael.uplawski@uplawski.eu>
334
+
335
+ Ω
336
+ ==
@@ -0,0 +1,2 @@
1
+ # UNUSED
2
+ " > >> > testerei ei ei".strip.gsub( /^>+(\s+>+)*/) {|m| m.to_s.delete(" ")}
@@ -0,0 +1,202 @@
1
+ #!/bin/env ruby
2
+ #encoding: UTF-8
3
+ =begin
4
+ /***************************************************************************
5
+ * 2023-2024, Michael Uplawski <michael.uplawski@uplawski.eu> *
6
+ * This program is free software; you can redistribute it and/or modify *
7
+ * it under the terms of the WTFPL 2.0 or later, see *
8
+ * http://www.wtfpl.net/about/ *
9
+ * *
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. *
13
+ * *
14
+ ***************************************************************************/
15
+ =end
16
+
17
+ #
18
+ # Simplified logging.
19
+ # See example code at the bottom of this file.
20
+ # Execute this file to see the output.
21
+ module BasicLogging
22
+
23
+ DEBUG = 0
24
+ INFO = 1
25
+ WARN = 2
26
+ ERROR = 3
27
+ FATAL = 4
28
+ UNKNOWN = nil
29
+
30
+ # this is mainly for the translation of method calls into log levels
31
+ Levels = {:debug => DEBUG, :info => INFO, :warn => WARN, :error => ERROR,
32
+ :fatal => FATAL, :unknown => UNKNOWN}
33
+
34
+ @@log_level = UNKNOWN
35
+ @@target = STDOUT
36
+ @@muted = []
37
+
38
+ # do not log, if caller is obj (class or instance)
39
+ def self.mute(obj)
40
+ name = obj.class == Class ? obj.name.dup : obj.class.name
41
+ @@muted << name
42
+ end
43
+
44
+ def self.is_muted?(obj)
45
+ name = obj.class == Class ? obj.name.dup : obj.class.name
46
+ @@muted.include?(name)
47
+ end
48
+
49
+ # set the log level
50
+ def set_level(lv)
51
+ if lv.respond_to?(:to_str) && Levels.keys.include?(lv.strip.to_sym)
52
+ lv = Levels[lv.to_sym]
53
+ elsif lv.respond_to?(:to_sym) && Levels.keys.include?(lv)
54
+ lv = Levels[lv]
55
+ end
56
+
57
+ if(!lv || (lv.respond_to?(:to_int) && lv >= DEBUG && lv <= FATAL) )
58
+ @@log_level = lv
59
+ else
60
+ msg = __FILE__.dup << ": ERROR : invalid log level \"" << lv.to_s << "\""
61
+ msg << "\n" << "Keepinng old log level " << Levels.keys.detect {| k| Levels[k] == @@log_level}.to_s
62
+ STDERR.puts msg
63
+ puts msg
64
+ end
65
+ end
66
+
67
+ # set the log target
68
+ def set_target(tg)
69
+ if tg.respond_to?(:to_io)
70
+ @@target = tg
71
+ elsif(!File::exist?(tg) || ( File.file?(tg) && File.writable?(tg) ) )
72
+ @@target = File.open(tg, 'w+')
73
+ elsif !tg || tg.respond_to?(:to_str) && tg.strip.empty?
74
+ @@target = nil
75
+ else
76
+ STDERR.puts __FILE__.dup << ': ERROR : target ' << tg << ' cannot be set'
77
+ STDERR.puts "Keeping old target " << @@target.inspect
78
+ return
79
+ end
80
+ end
81
+
82
+ # Output of log messages, depending on the log level set for the calling class
83
+ # and the name of the alias method which is actually called.
84
+ def log(message)
85
+ if !BasicLogging.is_muted?(self)
86
+ # how has this method been called?
87
+ mlevel = __callee__
88
+ if Levels.has_key?(mlevel) && Levels[mlevel] <= FATAL
89
+ # output only for levels equal or above the value that corresponds to
90
+ # the calling alias.
91
+ format_log( message, mlevel) if @@log_level && Levels[mlevel] >= @@log_level
92
+ else
93
+ STDERR.puts __FILE__.dup << ": ERROR : invalid log level \"" << mlevel.to_s << "\""
94
+ end
95
+ end
96
+ end
97
+
98
+ def target
99
+ @@target.path if @@target
100
+ end
101
+
102
+ def level
103
+ @@level.to_s if @@level
104
+ end
105
+
106
+ # Clear the log (-file)
107
+ def clear_log
108
+ if @@target && @@target.respond_to?(:truncate)
109
+ lock_target{ @@target.truncate(0) }
110
+ end
111
+ end
112
+
113
+ alias :debug :log
114
+ alias :info :log
115
+ alias :warn :log
116
+ alias :error :log
117
+ alias :fatal :log
118
+
119
+
120
+ private
121
+
122
+ def lock_target(&block)
123
+ begin
124
+ if @@target.respond_to?(:flock)
125
+ @@target.flock(File::LOCK_EX)
126
+ block.call
127
+ @@target.flock(File::LOCK_UN)
128
+ elsif @@target.respond_to?(:to_io)
129
+ block.call
130
+ end
131
+ rescue => ex
132
+ STDERR.puts __FILE__.dup << ": ERROR : cannot lock target (" << ex.message << ")"
133
+ end
134
+ end
135
+
136
+ # 1 format_log for all loggers.
137
+ def format_log(message, mlevel)
138
+ if @@target
139
+ # indicate if a registered class or the registered object of a class is calling.
140
+ name = self.class == Class ? self.name.dup << ' [class]' : self.class.name
141
+ lock_target{@@target.puts '' << name << ' ' << mlevel.to_s << ' ' << Time.now.strftime("%H:%M:%S:%6N") << ': ' << message.gsub("\n", "\n |")}
142
+ end
143
+ end
144
+ end
145
+ #---------test: execute file----------
146
+ if $0 == __FILE__
147
+ Array.extend(BasicLogging)
148
+ Array.set_level(BasicLogging::INFO)
149
+ Array.info('TEST')
150
+ ar = Array.new
151
+ ar.extend(BasicLogging)
152
+ # --- no output :
153
+ l = __LINE__
154
+ ar.debug(l.next.to_s << ': debug-test 0')
155
+ # output
156
+ ar.set_level(BasicLogging::DEBUG)
157
+ l = __LINE__
158
+ ar.debug(l.next.to_s << ': debug-test 1')
159
+
160
+ obj = Object.new
161
+ obj.extend(BasicLogging)
162
+ obj.set_level(BasicLogging::DEBUG)
163
+ puts "--------debug-----------"
164
+ obj.debug('debug')
165
+ obj.info('info')
166
+ obj.warn('warn')
167
+ obj.error('error')
168
+ obj.fatal('fatal')
169
+ puts "--------info-----------"
170
+ obj.set_level("info")
171
+ obj.debug('debug')
172
+ obj.info('info')
173
+ obj.warn('warn')
174
+ obj.error('error')
175
+ obj.fatal('fatal')
176
+ puts "--------fatal-----------"
177
+ obj.set_level("fatal")
178
+ obj.debug('debug')
179
+ obj.info('info')
180
+ obj.warn('warn')
181
+ obj.error('error')
182
+ obj.fatal('fatal')
183
+ puts "--------UNKNOWN-----------"
184
+ obj.set_level(nil)
185
+ obj.debug('debug')
186
+ obj.info('info')
187
+ obj.warn('warn')
188
+ obj.error('error')
189
+ obj.fatal('fatal')
190
+ puts " ------ Output into file ----"
191
+ obj.set_target "/tmp/test_log.log"
192
+ puts " ------ INFO -----------"
193
+ obj.set_level BasicLogging::INFO
194
+ obj.info('info output')
195
+
196
+ obj.info('info output 2')
197
+ puts "---------- invalid -------"
198
+ obj.set_target "/dev/sr0"
199
+ obj.set_level "power"
200
+ end
201
+
202
+ # EOF