Rebol [ file: %rebolbot.r3 author: "Graham" date: [28-Feb-2013 6-Apr-2013] ; leave this as a block plz! It's used by version command version: 0.0.31 purpose: {post messages into the Rebol-red chat room on Stackoverflow} Notes: {You'll need to capture your own cookie and fkey using wireshark or similar.} License: 'Apache2 ] ;-- optionally load my patched version of prot-http.r ; do %prot-my-http.r ; load some utilities if not value? 'load-json [ do http://reb4.me/r3/altjson ] if not value? 'decode-xml [ do http://reb4.me/r3/altxml ] if not value? 'shrink [ shrink: load http://www.rebol.org/download-a-script.r?script-name=shrink.r eliza: make object! shrink/4 eliza/rules: shrink/6 ] chat-length-limit: 500 ; SO chat limits to 500 chars if a message contains a link greet-message: " Welcome to the Rebol and Red room. See our [FAQ](https://github.com/hostilefork/r3-hf/wiki/StackOverflow-Chat-FAQ)" bot-cookie: {-get-your-own-} bot-fkey: "-get-your-own-" ; ideone API details ideone-user: "rebolbot" ideone-pass: "-get-your-own-" pause-period: 5 ; 5 seconds between each poll of the chat no-of-messages: 5 ; fetch 5 messages each time max-scan-messages: 200 ; max to fetch to scan for links by a user ; these users can remove keys - uses userids, the names are there just so that you know who they are! privileged-users: ["BrianH" 2016426 "HostileFork" 211160 "Graham Chiu" 76852 "GrahamChiu" 76852 "rgchris" 292969 "Adrian" 1792095 "dockimbel" 2026582 "earl" 135724] expressions: %bot-expressions.r notable-persons-file: %known-users.r visitors-file: %visitors.r visitors: copy [] lastmessage-no: 7973980 last-message-file: %lastmessage-no.r if exists? last-message-file [ attempt [ lastmessage-no: load last-message-file ] ] ?? lastmessage-no ; save/all %bot-config.r make object! compose [ bot-fkey: (bot-fkey) bot-cookie: (bot-cookie) ] if exists? %bot-config.r [ bot-config: load %bot-config.r bot-cookie: bot-config/bot-cookie bot-fkey: bot-config/bot-fkey ] bot-expressions: [ "help" ["FAQ" http://rebolsource.net/go/chat-faq] "tutorial" ["Introduction to Rebol" http://www.rebol.com/rebolsteps.html] "Devcon" ["Red Video from Devcon 2013" https://www.youtube.com/watch?v=JjPKj0_HBTY] ] ; save expressions bot-expressions if exists? expressions [ bot-expressions: load expressions ] ;- configuration urls remote-execution-url: [ rebol3 http://tryrebol.esperconsultancy.nl/do/REBOL rebol2 http://tryrebol.esperconsultancy.nl/do/REBOL-2 boron http://tryrebol.esperconsultancy.nl/do/Boron red http://tryrebol.esperconsultancy.nl/do/Red ] room-id: 291 room-descriptor: "rebol-and-red" id-rule: charset [#"0" - #"9"] so-chat-url: http://chat.stackoverflow.com/ chat-target-url: rejoin write-chat-block: [so-chat-url 'chats "/" room-id "/" 'messages/new] referrer-url: rejoin [so-chat-url 'rooms "/" room-id "/" room-descriptor] html-url: rejoin [referrer-url "?highlights=false"] read-target-url: rejoin [so-chat-url 'chats "/" room-id "/" 'events] delete-url: [so-chat-url 'messages "/" (parent-id) "/" 'delete] ; POST /messages/8034726/delete HTTP/1.1 ideone-url: http://ideone.com:80/api/1/service ; config botname botname: "@RebolBot" non-space: complement space: charset #" " percent-encode: func [char [char!]] [ char: enbase/base to-binary char 16 parse char [ copy char some [char: 2 skip (insert char "%") skip] ] char ] url-encode: use [ch mk] [ ch: charset ["-." #"0" - #"9" #"A" - #"Z" #"-" #"a" - #"z" #"~"] func [text [any-string!]] [ either parse/all text: form text [ any [ some ch | end | change " " "+" | mk: (mk: percent-encode mk/1) change skip mk ] ] [to-string text] [""] ] ] ; updated to remove the /local pad to-itime: func [ {Returns a standard internet time string (two digits for each segment)} time [time! number! block! none!] ] [ time: make time! time rejoin [ next form 100 + time/hour ":" next form 100 + time/minute ":" next form 100 + round/down time/second ] ] to-idate: func [ "Returns a standard Internet date string." date [date!] /local str ] [ str: form date/zone remove find str ":" if (first str) <> #"-" [insert str #"+"] if (length? str) <= 4 [insert next str #"0"] reform [ pick ["Mon," "Tue," "Wed," "Thu," "Fri," "Sat," "Sun,"] date/weekday date/day pick ["Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"] date/month date/year to-itime any [date/time 0:00] str ] ] ; perhaps not all of this header is required header: compose [ Host: "chat.stackoverflow.com" Origin: "http://chat.stackoverflow.com" Accept: "application/json, text/javascript, */*; q=0.01" X-Requested-With: "XMLHttpRequest" Referer: (referrer-url) Accept-Encoding: "gzip,deflate" Accept-Language: "en-US" Accept-Charset: "ISO-8859-1,utf-8;q=0.7,*;q=0.3" Content-Type: "application/x-www-form-urlencoded" cookie: (bot-cookie) ] soap-execute-template: {$a$b$c$d$e$f$g} soap-response-template: {$user$pass$link11111 } evaluate-by-ideone: func [message-id user pass source [string!] language [word! string! integer!] inpt [string!] /local result result2 error status link inputs output ] [ error: status: link: none ;print "in eval ideone" ;?? source source: head remove source head remove back tail source ;?? source if not integer? language [ language: select [ "forth" 107 "ruby" 17 "javascript" 35 "scheme" 33 "python" 4 "perl" 3 ] to string! language ] if none? language [ reply message-id "Unsupported language" return ] ;?? user ?? pass ?? source ?? language ?? inpt print reword soap-execute-template reduce [ 'a user 'b pass 'c source 'd language 'e inpt 'f "1" 'g "1" ] result: write ideone-url reduce ['SOAP ( reword soap-execute-template reduce [ 'a user 'b pass 'c source 'd language 'e inpt 'f "1" 'g "1" ] ) ] ; should get an error code probe decode 'markup result if parse decode 'markup result [ thru copy error to thru copy status to thru "link" copy link to to end] [ if all [ error/1 = "error" status/1 = "OK" ] [ ; we have a link value to get the result probe reword soap-response-template reduce [ 'user user 'password pass 'link link/1 ] ; wait before picking up the result wait 2 result2: write ideone-url reduce ['SOAP ( reword soap-response-template reduce [ 'user user 'pass pass 'link link/1 ] ) ] if result2 [ if parse decode 'markup result2 [ thru "source" thru copy inputs to thru "output" thru copy output to to end ] [ reply message-id rejoin [ " RebolBot uses http://ideone.com (c) http://sphere-research.com" newline " " decode-xml inputs/1 newline " " decode-xml output/1 ] ] ] ] ] ] speak-private: func [message room-id] [ bind write-chat-block 'room-id probe rejoin compose copy write-chat-block to string! write rejoin compose copy write-chat-block compose/deep copy/deep [ POST [(header)] (rejoin ["text=" url-encode message "&fkey=" bot-fkey]) ] ] speak: func [message /local err] [ if error? set/any 'err try [ to string! write chat-target-url compose/deep copy/deep [ POST [(header)] (rejoin ["text=" url-encode message "&fkey=" bot-fkey]) ] ] [ mold err ] ] read-messages: func [cnt] [ to string! write read-target-url compose/deep copy/deep [ POST [(header)] (rejoin ["since=0&mode=Messages&msgCount=" cnt "&fkey=" bot-fkey]) ] ] reply: func [message-id text [string! block!]] [ if block? text [text: ajoin text] speak ajoin [":" message-id " " text] ] provide-help: func [message-id] [ reply message-id {I respond to these commands: delete [ silent ] "in reply to a bot message will delete if in time" do expression "evaluates Rebol expression in a sandboxed interpreter (/x)" find descript [string! word!] "shows links with description containing descript" help "this help (/? and /h)" keys "returns known keys (/k)" present[?] "prints users currently online" remove key "removes key (authorized user) (/rm)" save my details url! [ timezone [time!]] "saves your details with url +/- timezone" save key [string! word!] description [string!] link [url!] "save key with description and link (/s)" show [all ][ recent ] links by user "shows links posted in messages by user" show links [ like url ] "shows saved links" show me your youtube videos "shows saved youtube videos" who is user "returns user details and page" whom do you know "returns a list of all known users" ? key [ for user | @user ] "Returns link and description" version "version of bot (/v)" } ] show-keys: func [message-id /local tmp out] [ tmp: copy [] out: copy "" foreach [key data] bot-expressions [ repend tmp [key data/1] ] sort/skip tmp 2 foreach [key description] tmp [ repend out ajoin [key { "} description {"^/}] ] reply message-id compose ["I know the following keys: ^/" (out)] ] save-key: func [message-id content [string! block!] /local exp err] [ if error? err: try [ exp: to block! content ?? exp either all [ any [string? exp/1 word? exp/1] exp/1: trim to string! exp/1 3 <= length? exp/1 ; no keywords of 1 2 characters string? exp/2 url? exp/3 ] [ print "okay to add" either not find bot-expressions exp/1 [ print "adding" append bot-expressions exp/1 repend/only bot-expressions [exp/2 exp/3] save expressions bot-expressions reply message-id ["added key: " exp/1] ] [ reply message-id [exp/1 " is already a key"] ] ] [ reply message-id [content " can not be saved as key"] ] ] [ probe mold err reply message-id mold err ] ] remove-key: func [message-id person person-id [integer!] content users [block!] /local rec ] [ either find users person-id [ ; privileged user either rec: find bot-expressions content [ remove/part rec 2 save expressions bot-expressions reply message-id ["removed " content] ] [ reply message-id [content " not found in my keys"] ] ] [ reply message-id ["Sorry, " person " you don't have the privileges yet to remove the key " content] ] ] ; mini-http is a minimalistic http implementation mini-http: func [ url [url!] method [word! string!] code [string!] timeout [integer!] /local url-obj http-request payload result port ][ http-request: {$method $path HTTP/1.0 Host: $host User-Agent: Mozilla/5.0 Accept: text/html Referer: http://$host Content-Length: $len Content-Type: text/plain; charset=UTF-8 $code} url-obj: construct/with sys/decode-url url make object! copy [port-id: 80 path: ""] if empty? url-obj/path [ url-obj/path: copy "/" ] payload: reword http-request reduce [ 'method method 'path url-obj/path 'host url-obj/host 'len length? code 'code code ] port: make port! rejoin [tcp:// url-obj/host ":" url-obj/port-id] port/awake: func [event] [ switch/default event/type [ lookup [open event/port false ] connect [write event/port to binary! join payload newline false] wrote [read event/port false] read done [ ; probe event/port/data result: to-string event/port/data true ] ][ true ] ] open port either port? wait [ port timeout ][ result ][ ; timeout none ] ] raw-read: func [ message-id target [url!] /local result err ][ if error? set/any 'err try [ either result: mini-http target 'GET "" 60 [ reply message-id result ][ reply message-id "HTTP timeout" ] ][ reply message-id mold err ] ] extract-http-response: func [ http-text [string!] /local result code bodytext server-code ][ digit: charset [ #"0" - #"9" ] either parse http-text [ thru "HTTP/1." [ "0" | "1" ] some space copy code 3 digit some space copy server-code to newline thru "^/^/" copy bodytext to end ][ trim/head/tail bodytext ][ make object! compose [ error: (server-code) code: (code) ] ] ] evaluate-expression: func [message-id expression /r2 "rebol2" /boron "boron" /red "RED" /local output html error-url exp execute-url ] [ output: html: error-url: none execute-url: select remote-execution-url case [ r2 ['rebol2] boron ['boron] red ['red] true ['rebol3] ] print ["attempting evaluation at: " execute-url] ;; html: to string! write execute-url compose [ POST (expression) ] ;; -- this begins the change from using native http if none? html: mini-http execute-url 'POST form expression 60 [ speak "tryrebol server timed out" return ] ; speak form type? html if object? html: extract-http-response html [ print "html is object!" speak mold html return ] ;; --- and ends the change from using native http scheme parse html [thru thru
 copy output to 
] output: decode-xml output ; if an error, remove part of the error string and parse out the help page if find output "*** ERROR" [ replace output "try do either either either -apply-" "" parse html [thru {> " trim expression newline " " output ] ?? expression ] was-about-users: [ earl [https://github.com/earl 1:00] graham [https://github.com/gchiu/ 13:00] ladislav [https://github.com/ladislav 1:00] rgchris [http://reb4.me/ none] hostilefork [http://hostilefork.com/ -5:00] brianH [https://github.com/BrianHawley -5:00] dockimbel [https://github.com/dockimbel 1:00] cyphre [https://github.com/cyphre 1:00] ] either exists? notable-persons-file [ about-users: load notable-persons-file ; check for old style file if url! = type? about-users/2 [ use [tmp tz rec] [ tmp: copy about-users clear head about-users foreach [user url] tmp [ append about-users user tz: either rec: select was-about-users user [ rec/2 ] [none] repend/only about-users [url tz] ] save notable-persons-file about-users ] ] ] [ about-users: copy was-about-users ] ;; -- compile a list of known people either not exists? visitors-file [ visitors: copy [] foreach [user data] about-users [ append visitors form user ] save visitors-file visitors ] [ visitors: load visitors-file ] ; pass the message to delete ; delete-url: [ so-chat-url 'messages "/" (message-id) 'delete ] delete-message: func [parent-id message-id /silent /local result mess ] [ mess: rejoin compose copy delete-url ?? mess result: to string! write mess: rejoin compose copy delete-url compose/deep copy/deep [ POST [(header)] (rejoin ["fkey=" bot-fkey]) ] if not silent [ switch/default result [ {"It is too late to delete this message"} [reply message-id ["sorry, it's too late to do this now. Be quicker next time"]] {"ok"} [reply message-id ["done"]] ] [ reply message-id ["SO says: " result] ] ] ] add-user-details: func [message-id person user-url timezone [time! none!] /local rec ] [ attempt [ person: to word! person if rec: find about-users person [ remove/part rec 2 ] repend about-users person repend/only about-users [user-url timezone] save notable-persons-file about-users reply message-id ajoin ["Added " person "'s details"] ] ] ; silent is used by the forever loop to update the users online who-is-online: func [message-id /silent /local out page username userid len newbies addressees ] [ addressees: copy "" len: length? visitors out: copy [] newbies: copy [] page: to string! read html-url parse page [ some [ thru "chat.sidebar.loadUser(" copy userid some id-rule thru {("} copy username to {")} (trim/all username username: decode-xml username append out username if not find visitors username [ append visitors username append newbies username ] ) ] to end ] either empty? out [ reply message-id "can not parse the page for users" ] [ either not silent [ reply message-id form out ] [ ; silent scan has detected new users - so let's greet them if not empty? newbies [ foreach person newbies [ append addressees ajoin ["@" person " "] ] speak ajoin [addressees " " greet-message] ] ] if len < length? visitors [ save visitors-file visitors ] ] ] show-all-users: func [message-id /local tmp ] [ tmp: copy [] foreach [user address] about-users [ append tmp user ] reply message-id join "I know something of the following people: " form sort tmp ] ; person is the one asking the question show-user-page: func [message-id user person /local data known timezone gmt err] [ gmt: now gmt/zone: 0:00 gmt: gmt - now/zone known: false user: to string! user attempt [trim/all person known: find about-users to word! person] if #"?" = last user [remove back tail user] if error? set/any 'err try [ either data: select about-users to word! user [ reply message-id ajoin [ "I know this about [" user "](" data/1 ") and their local time is " either time? timezone: data/2 [gmt + timezone] [ "unknown." ] ] ] [ reply message-id ["Sorry, I don't know anything about " user " yet."] ] if not known [ reply message-id ["I'd like to know about you! Use the 'save my details' command"] ] ] [ probe err ] ] ; find-in-links message-id form findstring find-in-links: func [message-id findstring /local out used link ] [ either 3 > length? findstring [ reply message-id "Find string needs to be at least 3 characters" ] [ out: copy "" used: copy [] foreach [key data] bot-expressions [ if all [ not find used data/2 find data/1 findstring ] [ link: ajoin ["[" data/1 "](" data/2 "); "] either chat-length-limit < add length? out length? link [ reply message-id out wait 2 out: copy link ] [ append out link ] append used data/2 ] ] if empty? out [out: copy "nothing found"] reply message-id out ] ] ; SO chat has a 500 character limit for messages with active links ; so let's send in 500 ( chat-length-limit ) char chunks ; this should be a refinement of show-similar-links show-all-links: func [message-id /local out link used] [ out: copy "" used: copy [] foreach [key data] bot-expressions [ if not find used data/2 [ link: ajoin ["[" data/1 "](" data/2 "); "] either chat-length-limit < add length? out length? link [ ; over chat-length-limit so send what we have reply message-id out wait 2 out: copy link ] [append out link] append used data/2 ] ] wait 2 if empty? out [out: copy "nothing found"] reply message-id out ] show-similar-links: func [message-id links /local out link tot used] [ print "in the simlar links function now" out: copy "" used: copy [] foreach [key data] bot-expressions [ if not find used data/2 [ if find/part data/2 links length? links [ link: ajoin ["[" data/1 "](" data/2 "); "] ; if adding a new link exceeds allowed, then send current either chat-length-limit < tot: add length? out length? link [ reply message-id out wait 2 ; and reset out to the new link out: copy link ] [ append out link ] append used data/2 ] ] ] wait 2 ;?? out if empty? out [out: copy "nothing found"] reply message-id out ] reply-time: func [message-id] [ reply message-id to-idate now ] process-dialect: funct [message-id person person-id expression ] [ show-rule: [ 'show any ['me | 'all] [ 'links (show-urls: true) opt ['like set links url! (similar: true)] | 'your 'youtube 'videos (youtube: true) ] ] whois-rule: [ [some ['who 'is | 'whois | 'who 'the 'dickens 'is] copy user to end ] (if found? user [show-user-page message-id user/1 person] done: true) ] whom-rule: ['whom 'do 'you ['know | 'know?] (show-all-users message-id done: true)] save-rule: [ (print "save rule" trim/all person ) 'save 'my 'details set user-url url! ( ?? user-url add-user-details message-id person user-url none done: true ) set user-timezone time! ( add-user-details message-id person user-url user-timezone ) ] save-key-rule: ['save copy expression to end (done: true save-key message-id expression)] do-rule: ['do copy expression to end (done: true attempt [ evaluate-expression message-id mold/only expression ] ) ] do2-rule: [['do/2 | 'do/rebol2] copy expression to end (done: true attempt [ evaluate-expression/r2 message-id mold/only expression ] ) ] do-boron-rule: ['do/boron copy expression to end (done: true attempt [ evaluate-expression/boron message-id mold/only expression ] ) ] do-red-rule: ['do/red copy expression to end (done: true attempt [ evaluate-expression/red message-id mold/only expression ] ) ] do-ideone-rule: ['do/ideone [set language word! | set language string! | set language integer!] copy expression to end (done: true attempt [ probe mold/only expression evaluate-by-ideone message-id ideone-user ideone-pass mold/only expression language "" ] ) ] version-rule: [ 'version (done: true reply message-id ajoin [system/script/header/version " " last system/script/header/date]) ] help-rule: ['help (done: true provide-help message-id)] key-rule: ['keys (done: true show-keys message-id)] remove-key-rule: [; remove-key message-id person person-id expression privileged-users 'remove copy expression to end ( done: true ?? message-id ?? person ?? person-id ?? expression remove-key message-id person person-id form expression privileged-users ) ] greet-rule: [copy greeting ['hello | 'goodbye | 'morning] (reply message-id [greeting " to you too"] done: true)] default-rule: [ ; default .. checks for a word and sends it to the check-keys [set search-key word! | set search-key string!] opt ['for set recipient word!] ( done: true ?? search-key ?? recipient either found? recipient [ recipient: ajoin ["@" recipient] ] [ recipient: copy "" ] process-key-search message-id trim ajoin [search-key " " recipient] ) ] search-key-rule: [ '? default-rule ] delete-rule: [ 'delete (done: true silent: false) opt [copy silent word!] ( either all [block? silent silent/1 = 'silent] [ delete-message/silent parent-id message-id ] [ print "not calling silent" delete-message parent-id message-id ] ) ] time-rule: [ 'what 'is 'the ['time | 'time?] opt ['now? | 'now | 'in 'GMT] (done: true reply-time message-id) ] life-rule: [ 'what 'is 'the 'meaning 'of ['life | 'life?] (done: true reply message-id "42" ) ] show-links-by-rule: [ opt 'show opt 'me opt 'recent 'links ['by | 'from] [set username word! | set username string!] ( done: true find-links-by message-id max-scan-messages username ) ] private-session-rule: [ 'private 'session 'in set private-room integer! ( done: true attempt [ reply message-id "OK, coming" wait 2 speak-private "hello" private-room ] ) ] find-rule: [ 'find [set findstring string! | set findstring word!] ( done: true find-in-links message-id form findstring ) ] who-is-online-rule: [ ['present | 'present?] ( done: true who-is-online message-id ) ] read-raw-rule: [ 'read 'raw set target url! ( done: true raw-read message-id target ) ] dialect-rule: [ ( recipient: none ) show-links-by-rule | show-rule | whois-rule | whom-rule | save-rule | save-key-rule | search-key-rule | do-rule | do2-rule | do-boron-rule | do-red-rule | do-ideone-rule | version-rule | help-rule | key-rule | remove-key-rule | greet-rule | delete-rule | time-rule | life-rule | private-session-rule | find-rule | who-is-online-rule | read-raw-rule | default-rule ] show-urls: similar: links: youtube: done: false tmp: copy "" if error? err: try [ ; what to do about illegal rebol values eg @Graham if error? err2: try [ to block! expression ] [ if find mold err2 {arg1: "email"} [ replace/all expression "@" "" ] ] parse expression: to block! expression dialect-rule ;?? expression ;?? similar ;?? show-urls case [ similar [ show-similar-links message-id links ] show-urls [ show-all-links message-id ] youtube [ show-similar-links message-id https://www.youtube.com wait 2 show-similar-links message-id http://www.youtube.com ] done [] ; true [ reply message-id [ "Sorry, don't understand " expression ]] ; replace by using Eliza true [ reply message-id eliza/match mold expression ] ] ] [ ; sends error ; reply message-id mold err ; now uses Eliza reply message-id eliza/match mold expression ] ] process-bot-cmd: func [person person-id message-id cmd expression] [ switch/default cmd [ "?" "h" [provide-help message-id] "d" [process-dialect message-id person person-id expression] "k" [show-keys message-id] "rm" [remove-key message-id person person-id expression privileged-users] "s" [save-key message-id expression] "v" [reply message-id form system/script/header/version] "x" [attempt [evaluate-expression message-id expression]] ] [ ; unknown command - object ; reply message-id [ cmd " is not in my repertoire yet." ] reply message-id eliza/match mold expression ] ] process-key-search: func [message-id expression /local understood search-key person ] [ understood: false set [search-key person] parse expression none unless all [ person parse person ["@" to end] ] [person: none] ; remove punctuation of ! and ? if find [#"!" #"?"] last search-key [remove back tail search-key] foreach [key data] bot-expressions [ if find/part key search-key length? search-key [ understood: true reply message-id ["[" data/1 "](" data/2 ") " either found? person [person] [""]] break ] ] if not understood [ ;reply message-id [ {sorry "} expression {" is not in my current repertoire. Try /h for help} ] reply message-id eliza/match mold expression ] ] ; cmd is k, rm, s etc, and expression is either "" or something like "print 1 + 2" bot-cmd-rule: [ botname some space [ "/" copy cmd some non-space [ end (expression: copy "") | some space copy expression to end (trim expression) ] ( process-bot-cmd user-name person-id message-id cmd expression) | ; some keyword or dialected command follows copy key to end ( ; process-key-search message-id trim key process-dialect message-id user-name person-id key ) ] ] message-rule: [ quote 1 | integer! | set content string! | integer! | set person-id integer! | set user-name string! | integer! | string! | set message-no integer! | set parent-id integer! | logic! | tag! skip | end ] result: messages: parent-id: none ; lastmessage-no: 7999529 read-messages-by: func [n username /local result messages wanted user ] [ wanted: copy [] username: form username result: load-json/flat read-messages n messages: result/2 foreach msg messages [ if parse msg [some [thru copy content string! | thru copy user string! to end]] [ if user/1 = username [ ; found a message we want append wanted content ] ] ] wanted ] find-links-by: func [message-id n username /local result links link ilink text payload ] [ links: copy [] result: read-messages-by n username ; now have a block of messages by username ; {this is a link rebol tech that I want to see} ;["this is a link " "rebol tech" " that I want to see"] ;{text} ; [ "text" ] foreach content result [ ; grab all links from the message parse decode 'markup to binary! decode-xml content [ some [ opt string! set link tag! set text string! ( if parse form link [thru {a href="} copy ilink to {"} to end] [ repend links [text ilink] ] ) opt string! ] ] ] ; we have all the links either empty? links [ reply message-id ["No links found in the last " n " messages."] ] [ payload: rejoin [username " in the last " n " messages wrote the following links: "] foreach [text link] links [ link: rejoin ["[" text "](" link "); "] either chat-length-limit < add length? payload length? link [ reply message-id payload wait 2 payload: copy link ] [ append payload link ] ] reply message-id payload ] ] cnt: 0 ; rescan for new users every 10 iterations ( for 5 seconds, that's 50 seconds ) forever [ ++ cnt if error? set/any 'errmain try [ result: load-json/flat read-messages no-of-messages messages: result/2 ; now skip thru each message and see if any unread foreach msg messages [ content: user-name: none message-no: 0 ?? msg either parse msg [some message-rule] [ print "parsed" ?? parent-id ] [print "failed"] message-id: message-no content: trim decode-xml content ?? content ?? user-name ?? person-id ?? message-no ?? lastmessage-no ; new message? if message-no > lastmessage-no [ print "New message" save last-message-file lastmessage-no: message-no ; {
@RebolBot /x a: "Hello"
print a
} ; {
@rebolbot
print "ehll"
} parse content [
opt space copy content to
( if parse content [botname #" "
to end] [ ; treat a newline after botname as a do-rule replace content
"do " ] replace/all content
" " trim content ) ] if parse content bot-cmd-rule [ print "message for me, we should have dealt with it in the parse rule" ] ] ] ] [ probe mold errmain ] if cnt >= 10 [ cnt: 0 ; scan the html page, check to see who is here, and send a greet message to new users who-is-online/silent 0 ] wait pause-period ] halt