polly.lsl 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. /*\ chat parrot v0.4.2
  2. |*|
  3. |*| TODO: get list of chatters
  4. \*/
  5. integer DEBUG = FALSE ;
  6. string SERVER_URL = "https://CHANGEME" ;
  7. integer SHOULD_VERIFY_SSL = TRUE ;
  8. string GRID_NAME = "CHANGEME" ;
  9. integer HAS_CUSTOM_NAMES = TRUE ; // does server allow arbitrary choice of last name
  10. string CLIENTS_PATH = "/clients.json" ;
  11. string PING_PATH = "/clients/{{CLIENT_NAME}}/ping.json" ;
  12. string ROOMS_PATH = "/clients/{{CLIENT_NAME}}/rooms.json" ;
  13. string BRIDGE_PATH = "/clients/{{CLIENT_NAME}}/bridge.json" ;
  14. string MESSAGE_PATH = "/clients/{{CLIENT_NAME}}/message.json" ;
  15. string MESSAGES_PATH = "/clients/{{CLIENT_NAME}}/messages.json" ;
  16. string OWNER_NAME ; // Init()
  17. string REGION_NAME ; // Init()
  18. string PARCEL_NAME ; // Init()
  19. string CLIENT_NAME ; // Init()
  20. string LOGIN_URL ; // Init()
  21. string PING_URL ; // Init()
  22. string ROOMS_URL ; // Init()
  23. string BRIDGE_URL ; // Init()
  24. string MESSAGE_URL ; // Init()
  25. string MESSAGES_URL ; // Init()
  26. string CLIENT_SECRET ; // ParseLoginResponse()
  27. string ROOM_SECRET ; // ParseLoginResponse()
  28. string NOTECARD_CLIENT_SECRET ; // ParseLoginResponse()
  29. // user messages and dialog prompts
  30. integer DLG_CH = -99 ;
  31. string OFF_DLG_BTN = "Off" ;
  32. string ON_DLG_BTN = "On" ;
  33. string BRIDGE_DLG_BTN = "Join Room" ;
  34. string INIT_MSG = "Connecting" ;
  35. string RESETTING_MSG = "Re-connecting" ;
  36. string READY_MSG = "Ready" ;
  37. string RUNNING_MSG = "Listening" ;
  38. string IDLE_MSG = "Not listening" ;
  39. string SERVER_ERROR_MSG = "Invalid server response" ;
  40. string INVALID_SECRET_MSG = "Client secret invalid - Try again later" ;
  41. string ROOMS_MSG = "The following public chat rooms are available to join:" ;
  42. string BRIDGE_PROMPT = "Type your choice in local chat (e.g. #42):" ;
  43. string NO_ROOMS_MSG = "There are no other rooms to join" ;
  44. string BRIDGE_SELECT_TIMEOUT = "Cancelling room join" ;
  45. string BRIDGE_FAIL_MSG = "Could not join room" ;
  46. string BRIDGE_TIMEOUT_MSG = "Timeout waiting to join room" ;
  47. string JOINED_ROOM_MSG = "Joined room with: " ;
  48. string DLG_RUNNING_PROMPT = "Polly wanna shuddup?" ;
  49. string DLG_IDLE_PROMPT = "Polly wanna cracker?" ;
  50. vector TEXT_COLOR = <1.0 , 1.0 , 0.0> ;
  51. // JSON string parsing
  52. string ID_SENTINAL = "{{CLIENT_NAME}}" ;
  53. string DUPLICATE_SECRET = "exists" ;
  54. string SECRET_TOKEN = "\"secret\":\"" ; integer SECRET_TOKEN_LEN = 10 ;
  55. string ROOM_SECRET_TOKEN = "\"room_secret\":\"" ; integer ROOM_SECRET_TOKEN_LEN = 15 ;
  56. string CLIENTS_TOKEN = "\"clients\":\"" ; integer CLIENTS_TOKEN_LEN = 11 ;
  57. string ID_TOKEN = "\"id\":" ; integer ID_TOKEN_LEN = 5 ;
  58. string NICK_TOKEN = "\"nick\":\"" ; integer NICK_TOKEN_LEN = 8 ;
  59. string TEXT_TOKEN = "\"text\":\"" ; integer TEXT_TOKEN_LEN = 8 ;
  60. string INT_SEP_TOKEN = ",\"" ;
  61. string STRING_SEP_TOKEN = "\",\"" ;
  62. string STRING_END_TOKEN = "\"}" ;
  63. // http
  64. integer HTTP_STATUS_SUCCESS = 200 ;
  65. integer HTTP_STATUS_UNAUTHORIZED = 401 ;
  66. list JSON_POST_PARAMS = [ HTTP_METHOD , "POST" ,
  67. HTTP_MIMETYPE , "application/json" ,
  68. HTTP_VERIFY_CERT , FALSE ] ;
  69. // timers
  70. float TIMEOUT = 1500.0 ;
  71. float SLOWPOLL = 300.0 ;
  72. float MEDPOLL = 60.0 ;
  73. float FASTPOLL = 2.0 ;
  74. float DLG_TIMEOUT = 30.0 ;
  75. // misc
  76. string NOTECARD_NAME = "secrets" ;
  77. string CLIENT_SECRET_KEY = "CLIENT_SECRET" ;
  78. integer N_ROOMS_PARAMS = 2 ;
  79. integer N_MESSAGE_PARAMS = 3 ;
  80. // runtime vars
  81. integer NotecardLineN = 0 ;
  82. list Rooms = [] ;
  83. list Messages = [] ;
  84. float PollRate = 0.0 ;
  85. integer LastDlg = 0 ;
  86. integer LastLocalChat = 0 ;
  87. integer LastRemoteChat = 0 ;
  88. integer LastMsgId = 0 ;
  89. integer ShouldResume = TRUE ;
  90. key NotecardReqId ; // default::state_entry()
  91. key LoginReqId ; // default::state_entry()
  92. key PingReqId ; // default::http_response()
  93. key MessagesReqId ; // ready::timer()
  94. key RoomsReqId ; // bridging::state_entry()
  95. key BridgeReqId ; // bridging::listen()
  96. key AdminId ; // ready::touch_start()
  97. integer ChatListener ; // ready::state_entry()
  98. integer CtrlListener ; // ready::touch_start()
  99. /* state default functions */
  100. Init()
  101. {
  102. if (OWNER_NAME == "") SayState(INIT_MSG) ; else SayState(RESETTING_MSG) ;
  103. list parcel_details = llGetParcelDetails(llGetPos() , [PARCEL_DETAILS_NAME]) ;
  104. OWNER_NAME = llKey2Name(llGetOwner()) ;
  105. REGION_NAME = StrReplace(llGetRegionName() , " " , "_") ;
  106. PARCEL_NAME = StrReplace(llList2String(parcel_details , 0) , " " , "_") ;
  107. CLIENT_NAME = llDumpList2String([ GRID_NAME , REGION_NAME , PARCEL_NAME ] , " ") ;
  108. PING_PATH = StrReplace(PING_PATH , ID_SENTINAL , llEscapeURL(CLIENT_NAME)) ;
  109. ROOMS_PATH = StrReplace(ROOMS_PATH , ID_SENTINAL , llEscapeURL(CLIENT_NAME)) ;
  110. BRIDGE_PATH = StrReplace(BRIDGE_PATH , ID_SENTINAL , llEscapeURL(CLIENT_NAME)) ;
  111. MESSAGE_PATH = StrReplace(MESSAGE_PATH , ID_SENTINAL , llEscapeURL(CLIENT_NAME)) ;
  112. MESSAGES_PATH = StrReplace(MESSAGES_PATH , ID_SENTINAL , llEscapeURL(CLIENT_NAME)) ;
  113. LOGIN_URL = llToLower(SERVER_URL) + CLIENTS_PATH ;
  114. PING_URL = llToLower(SERVER_URL) + PING_PATH ;
  115. ROOMS_URL = llToLower(SERVER_URL) + ROOMS_PATH ;
  116. BRIDGE_URL = llToLower(SERVER_URL) + BRIDGE_PATH ;
  117. MESSAGE_URL = llToLower(SERVER_URL) + MESSAGE_PATH ;
  118. MESSAGES_URL = llToLower(SERVER_URL) + MESSAGES_PATH ;
  119. JSON_POST_PARAMS = llListReplaceList(JSON_POST_PARAMS , SHOULD_VERIFY_SSL , -1 , -1) ;
  120. NotecardLineN = 0 ;
  121. Rooms = [] ;
  122. Messages = [] ;
  123. PollRate = 0.0 ;
  124. LastDlg = 0 ;
  125. LastLocalChat = 0 ;
  126. LastRemoteChat = 0 ;
  127. LastMsgId = 0 ;
  128. NotecardReqId = NULL_KEY ;
  129. LoginReqId = NULL_KEY ;
  130. RoomsReqId = NULL_KEY ;
  131. MessagesReqId = NULL_KEY ;
  132. BridgeReqId = NULL_KEY ;
  133. AdminId = llGetOwner() ;
  134. ChatListener = 0 ;
  135. CtrlListener = 0 ;
  136. }
  137. key SendLoginRequest() { return SendRequest(LOGIN_URL , "" , "" , "") ; }
  138. key SendPingRequest() { return SendRequest(PING_URL , "" , "" , "") ; }
  139. integer ParseLoginResponse(string body)
  140. {
  141. string client_secret ; string room_secret ;
  142. body = ParseSubstring(body , SECRET_TOKEN , SECRET_TOKEN_LEN ) ;
  143. client_secret = llGetSubString(body , 0 , llSubStringIndex(body , STRING_SEP_TOKEN) - 1) ;
  144. body = ParseSubstring(body , ROOM_SECRET_TOKEN , ROOM_SECRET_TOKEN_LEN) ;
  145. room_secret = llGetSubString(body , 0 , llSubStringIndex(body , STRING_END_TOKEN) - 1) ;
  146. if (client_secret == "" || room_secret == "") { SayState(SERVER_ERROR_MSG) ; return FALSE ; }
  147. DBG_DUPLICATE_SECRET(client_secret) ;
  148. if (client_secret == DUPLICATE_SECRET) client_secret = CLIENT_SECRET ;
  149. if (client_secret == "" ) client_secret = NOTECARD_CLIENT_SECRET ;
  150. if (client_secret == "" ) { SayState(INVALID_SECRET_MSG) ; return FALSE ; }
  151. CLIENT_SECRET = client_secret ;
  152. ROOM_SECRET = room_secret ;
  153. return TRUE ;
  154. }
  155. DisplaySecrets()
  156. {
  157. if (NOTECARD_CLIENT_SECRET != CLIENT_SECRET)
  158. AdminSay("## copy these lines into the notecard: '" + NOTECARD_NAME + "' ## " +
  159. "\nCLIENT_SECRET = " + (CLIENT_SECRET) +
  160. "\nROOM_SECRET = " + (ROOM_SECRET ) ) ;
  161. }
  162. /* state ready functions */
  163. integer QueueMessage(string name , key id , string msg)
  164. {
  165. if (llGetAgentSize(id) == ZERO_VECTOR) return FALSE ; // not an avatar
  166. if (HAS_CUSTOM_NAMES) name = StrReplace(name , " " , "_") ;
  167. else name = llList2String(llParseString2List(name , [" "] , []) , 0) ;
  168. Messages += [ name , msg , llGetUnixTime() ] ;
  169. return TRUE ;
  170. }
  171. SetPollRate(float poll_rate)
  172. {
  173. if (PollRate != poll_rate) { PollRate = poll_rate ; llSetTimerEvent(PollRate) ; }
  174. }
  175. Stop()
  176. {
  177. ShouldResume = !!ChatListener ;
  178. SayState(IDLE_MSG) ; llListenRemove(ChatListener) ; ChatListener = 0 ;
  179. }
  180. ThrottlePollRate(integer now)
  181. {
  182. if ((now - LastDlg) > DLG_TIMEOUT) { llListenRemove(CtrlListener) ; CtrlListener = 0 ; }
  183. if ((now - LastLocalChat ) > TIMEOUT) SetPollRate(SLOWPOLL) ;
  184. else if ((now - LastRemoteChat) > MEDPOLL) SetPollRate(MEDPOLL ) ;
  185. DBG_STATS(now) ;
  186. }
  187. PostMessages()
  188. {
  189. while (llGetListLength(Messages))
  190. {
  191. string nick = llList2String(Messages , 0) ;
  192. string message = llList2String(Messages , 1) ;
  193. Messages = llDeleteSubList(Messages , 0 , N_MESSAGE_PARAMS - 1) ;
  194. SendRequest(MESSAGE_URL , "" , nick , message) ;
  195. }
  196. }
  197. key SendMessagesRequest() { return SendRequest(MESSAGES_URL , "" , "" , "") ; }
  198. list ParseMessagesResponse(string body)
  199. {
  200. list messages = [] ; integer n_messages = 0 ; string id ; string nick ; string text ;
  201. do
  202. {
  203. body = ParseSubstring(body , ID_TOKEN , ID_TOKEN_LEN ) ;
  204. id = llGetSubString(body , 0 , llSubStringIndex(body , INT_SEP_TOKEN ) - 1) ;
  205. body = ParseSubstring(body , NICK_TOKEN , NICK_TOKEN_LEN) ;
  206. nick = llGetSubString(body , 0 , llSubStringIndex(body , STRING_SEP_TOKEN) - 1) ;
  207. body = ParseSubstring(body , TEXT_TOKEN , TEXT_TOKEN_LEN) ;
  208. text = llGetSubString(body , 0 , llSubStringIndex(body , STRING_SEP_TOKEN) - 1) ;
  209. if (id != "" && nick != "" && text != "") messages += [ id , nick , text ] ;
  210. DBG_PARSE_MSGS(id , nick , text) ;
  211. }
  212. while (n_messages < (n_messages = (llGetListLength(messages) / N_MESSAGE_PARAMS))) ;
  213. return messages ;
  214. }
  215. DisplayMessages(list messages)
  216. {
  217. integer message_n ; integer n_messages = llGetListLength(messages) / N_MESSAGE_PARAMS ;
  218. for (message_n = 0 ; message_n < n_messages ; ++message_n)
  219. {
  220. integer offfset = N_MESSAGE_PARAMS * message_n ;
  221. LastMsgId = llList2Integer(messages , offfset + 0) ;
  222. string nick = llList2String (messages , offfset + 1) ;
  223. string text = llList2String (messages , offfset + 2) ;
  224. llSay(PUBLIC_CHANNEL , llUnescapeURL(nick + ": " + text)) ;
  225. }
  226. }
  227. /* state bridging functions */
  228. key SendRoomsRequest() { return SendRequest(ROOMS_URL , "" , "" , "") ; }
  229. ParseRoomsResponse(string body)
  230. {
  231. Rooms = [] ; integer n_rooms = 0 ; string secret ; string clients_csv ;
  232. do
  233. {
  234. body = ParseSubstring(body , SECRET_TOKEN , SECRET_TOKEN_LEN ) ;
  235. secret = llGetSubString(body , 0 , llSubStringIndex(body , STRING_SEP_TOKEN) - 1) ;
  236. body = ParseSubstring(body , CLIENTS_TOKEN , CLIENTS_TOKEN_LEN) ;
  237. clients_csv = llGetSubString(body , 0 , llSubStringIndex(body , STRING_END_TOKEN) - 1) ;
  238. if (secret != "" && clients_csv != "") Rooms += [ secret , clients_csv ] ;
  239. DBG_PARSE_ROOMS(secret , clients_csv) ;
  240. }
  241. while (n_rooms < (n_rooms = (llGetListLength(Rooms) / N_ROOMS_PARAMS))) ;
  242. }
  243. integer PromptRooms()
  244. {
  245. integer room_n ; integer n_rooms = llGetListLength(Rooms) / N_ROOMS_PARAMS ;
  246. if (n_rooms == 0) return FALSE ;
  247. AdminSay(ROOMS_MSG) ;
  248. for (room_n = 0 ; room_n < n_rooms ; ++room_n)
  249. {
  250. string clients_csv = llUnescapeURL(llList2String(Rooms , (room_n * N_ROOMS_PARAMS) + 1)) ;
  251. string join = "\n " ;
  252. AdminSay("#" + (string)(room_n + 1) + ":" + join + StrReplace(clients_csv , "," , join)) ;
  253. }
  254. AdminSay(BRIDGE_PROMPT) ;
  255. return TRUE ;
  256. }
  257. key SendBridgeRequest(string room_secret) { return SendRequest(BRIDGE_URL , room_secret , "" , "") ; }
  258. ParseBridgeResponse(string body)
  259. {
  260. string room_secret ; string clients_csv ;
  261. body = ParseSubstring(body , ROOM_SECRET_TOKEN , ROOM_SECRET_TOKEN_LEN) ;
  262. room_secret = llGetSubString(body , 0 , llSubStringIndex(body , STRING_END_TOKEN) - 1) ;
  263. body = ParseSubstring(body , CLIENTS_TOKEN , CLIENTS_TOKEN_LEN) ;
  264. clients_csv = llGetSubString(body , 0 , llSubStringIndex(body , STRING_END_TOKEN) - 1) ;
  265. if (room_secret == "" || clients_csv == "") SayState(BRIDGE_FAIL_MSG) ;
  266. else SayState(JOINED_ROOM_MSG + clients_csv) ;
  267. }
  268. /* helpers */
  269. integer IsAdmin(key id) { return id == llGetOwner() ; }
  270. AdminSay(string msg) { llInstantMessage(AdminId , msg) ; }
  271. SayState(string msg) { llSay(PUBLIC_CHANNEL , msg) ; llSetText(msg , TEXT_COLOR , 1.0) ; }
  272. key SendRequest(string url , string room_secret , string nick , string message)
  273. {
  274. string client_key = "'client': " ;
  275. string secret_json = "{ 'secret': '" + CLIENT_SECRET + "' }" ;
  276. string client_params = secret_json ;
  277. string extra_params = "" ;
  278. string request_json ;
  279. if (url == LOGIN_URL )
  280. client_params = "{ 'name': '" + CLIENT_NAME + "' , " +
  281. " 'nicks': '" + OWNER_NAME + "' , " +
  282. " 'type': '" + "SimClient" + "' } " ;
  283. else if (url == ROOMS_URL ) ;
  284. else if (url == BRIDGE_URL )
  285. extra_params = " , 'room_secret': '" + room_secret + "'" ;
  286. else if (url == MESSAGE_URL )
  287. extra_params = " , 'message': { 'nick': '" + nick + "' , " +
  288. " 'text': '" + llEscapeURL(message) + "' } " ;
  289. else if (url == MESSAGES_URL)
  290. extra_params = " , 'last_id': '" + (string)LastMsgId + "'" ;
  291. request_json = "{ " + client_key + client_params + extra_params + " }" ;
  292. request_json = StrReplace(request_json , "'" , "\"") ;
  293. DBG_SEND_REQ(url , request_json) ;
  294. return llHTTPRequest(url , JSON_POST_PARAMS , request_json) ;
  295. }
  296. string StrReplace(string str , string sentinel , string replace)
  297. {
  298. return llDumpList2String(llParseStringKeepNulls(str , [sentinel] , []) , replace) ;
  299. }
  300. string ParseSubstring(string search_string , string token , integer offset)
  301. {
  302. integer idx ; string substring = "" ;
  303. if (~(idx = llSubStringIndex(search_string , token)))
  304. substring = llGetSubString(search_string , idx + offset , -1) ;
  305. return substring ;
  306. }
  307. /* debug */
  308. DBG(string dbg) { if (DEBUG) llOwnerSay(dbg) ; }
  309. DBG_STATS(integer now)
  310. {
  311. if (!DEBUG) return ;
  312. string polling_msg = "polling @ " + (string)((integer)PollRate ) + " sec" ;
  313. string local_msg = "LastLocalChat: " + (string)(now - LastLocalChat ) + " sec" ;
  314. string remote_msg = "LastRemoteChat: " + (string)(now - LastRemoteChat) + " sec" ;
  315. DBG("DBG_STATS() " + polling_msg + " - " + local_msg + " " + remote_msg) ;
  316. if (DBG_T = (++DBG_T % 5) < 3) llSetText(polling_msg + "\n" + local_msg + "\n" + remote_msg , TEXT_COLOR , 1.0) ;
  317. else if (ChatListener != 0 ) llSetText(RUNNING_MSG , TEXT_COLOR , 1.0) ;
  318. else if (ChatListener == 0 ) llSetText(IDLE_MSG , TEXT_COLOR , 1.0) ;
  319. } integer DBG_T = 0 ;
  320. DBG_DUPLICATE_SECRET(string client_secret)
  321. {
  322. if (client_secret != DUPLICATE_SECRET) return ;
  323. string dbg = "ParseLoginResponse() DUPLICATE_SECRET - trying " ;
  324. if (CLIENT_SECRET != "") DBG(dbg + "CLIENT_SECRET=" + CLIENT_SECRET ) ;
  325. else DBG(dbg + "NOTECARD_CLIENT_SECRET=" + NOTECARD_CLIENT_SECRET) ;
  326. }
  327. DBG_SEND_REQ(string url , string request_json)
  328. {
  329. DBG("SendRequest() url=" + url + "\nrequest_json=" + request_json) ;
  330. }
  331. DBG_PARSE_ROOMS(string secret , string clients_csv)
  332. {
  333. DBG("ParseRoomsResponse() secret='" + secret + "' clients_csv='" + clients_csv + "'") ;
  334. }
  335. DBG_PARSE_MSGS(string id , string nick , string text)
  336. {
  337. DBG("ParseMessagesResponse() id='" + id + "' nick='" + nick + "' text='" + text + "'") ;
  338. }
  339. default
  340. {
  341. state_entry()
  342. {
  343. Init() ;
  344. if (llGetInventoryKey(NOTECARD_NAME) != NULL_KEY)
  345. NotecardReqId = llGetNotecardLine(NOTECARD_NAME , NotecardLineN) ;
  346. else LoginReqId = SendLoginRequest() ;
  347. }
  348. dataserver(key resp_id , string data)
  349. {
  350. if (resp_id != NotecardReqId) return ;
  351. if (data != EOF)
  352. {
  353. list kvp = llParseString2List(data , ["="] , []) ;
  354. string k = llStringTrim(llList2String(kvp , 0) , STRING_TRIM) ;
  355. string v = llStringTrim(llList2String(kvp , 1) , STRING_TRIM) ;
  356. if (k == CLIENT_SECRET_KEY) NOTECARD_CLIENT_SECRET = v ;
  357. NotecardReqId = llGetNotecardLine(NOTECARD_NAME , ++NotecardLineN) ;
  358. }
  359. else LoginReqId = SendLoginRequest() ;
  360. }
  361. http_response(key resp_id , integer status , list data , string body)
  362. {
  363. if (resp_id == LoginReqId && status == HTTP_STATUS_SUCCESS)
  364. {
  365. LoginReqId = NULL_KEY ;
  366. if (ParseLoginResponse(body)) PingReqId = SendPingRequest() ;
  367. }
  368. else if (resp_id == PingReqId)
  369. {
  370. PingReqId = NULL_KEY ;
  371. if (status == HTTP_STATUS_SUCCESS ) state ready ;
  372. else if (status == HTTP_STATUS_UNAUTHORIZED) SayState(INVALID_SECRET_MSG) ;
  373. }
  374. }
  375. }
  376. state ready
  377. {
  378. state_entry()
  379. {
  380. if (ShouldResume) SayState(RUNNING_MSG) ; else { SayState(READY_MSG) ; return ; }
  381. ChatListener = llListen(PUBLIC_CHANNEL , "" , NULL_KEY , "" ) ;
  382. LastLocalChat = LastRemoteChat = llGetUnixTime() - (integer)MEDPOLL ;
  383. SetPollRate(FASTPOLL) ;
  384. }
  385. touch_start(integer n)
  386. {
  387. string dialog_prompt ;
  388. if (ChatListener == 0) dialog_prompt = DLG_IDLE_PROMPT ;
  389. else dialog_prompt = DLG_RUNNING_PROMPT ;
  390. key id = llDetectedKey(0) ;
  391. list dlg_btns = [ OFF_DLG_BTN , ON_DLG_BTN ] ;
  392. if (IsAdmin(id)) { dlg_btns += BRIDGE_DLG_BTN ; AdminId = id ; }
  393. llDialog(id , dialog_prompt , dlg_btns , DLG_CH) ;
  394. CtrlListener = llListen(DLG_CH , "" , id , "") ;
  395. LastDlg = llGetUnixTime() ;
  396. }
  397. listen(integer ch , string name , key id , string msg)
  398. {
  399. if (ch == PUBLIC_CHANNEL) { if (QueueMessage(name , id , msg)) SetPollRate(FASTPOLL) ; }
  400. else if (ch == DLG_CH )
  401. if (msg == BRIDGE_DLG_BTN && IsAdmin(id)) { Stop() ; state bridging ; }
  402. else if (msg == OFF_DLG_BTN ) { Stop() ; }
  403. else if (msg == ON_DLG_BTN ) { state default ; }
  404. }
  405. timer()
  406. {
  407. integer now = llGetUnixTime() ;
  408. if (llGetListLength(Messages) == 0) { ThrottlePollRate(now) ; }
  409. else { PostMessages() ; LastLocalChat = now ; }
  410. MessagesReqId = SendMessagesRequest() ;
  411. }
  412. http_response(key resp_id , integer status , list data , string body)
  413. {
  414. if (resp_id == MessagesReqId ) MessagesReqId = NULL_KEY ;
  415. if (status != HTTP_STATUS_SUCCESS) return ;
  416. DisplayMessages(ParseMessagesResponse(body)) ; LastRemoteChat = llGetUnixTime() ;
  417. }
  418. }
  419. state bridging
  420. {
  421. state_entry() { llSetTimerEvent(DLG_TIMEOUT) ; RoomsReqId = SendRoomsRequest() ; }
  422. http_response(key resp_id , integer status , list data , string body)
  423. {
  424. if (status != HTTP_STATUS_SUCCESS) return ;
  425. if (resp_id == RoomsReqId )
  426. {
  427. RoomsReqId = NULL_KEY ; ParseRoomsResponse(body) ;
  428. if (PromptRooms()) ChatListener = llListen(PUBLIC_CHANNEL , "" , AdminId , "") ;
  429. else llSetTimerEvent(1.0) ; // bail
  430. }
  431. else if (resp_id == BridgeReqId)
  432. {
  433. BridgeReqId = NULL_KEY ; ParseBridgeResponse(body) ; llSetTimerEvent(1.0) ; // done
  434. }
  435. }
  436. listen(integer ch , string name , key id , string msg)
  437. {
  438. if (!!llSubStringIndex(msg , "#")) return ;
  439. integer room_n = (integer)llGetSubString(msg , 1 , -1) - 1 ;
  440. integer n_rooms = llGetListLength(Rooms) / N_ROOMS_PARAMS ;
  441. string room_secret = llList2String(Rooms , (room_n * N_ROOMS_PARAMS) + 0) ;
  442. if (room_n < 0 || room_n >= n_rooms) return ;
  443. llListenRemove(ChatListener) ; ChatListener = 0 ;
  444. BridgeReqId = SendBridgeRequest(room_secret) ;
  445. }
  446. timer()
  447. {
  448. if (RoomsReqId != NULL_KEY || BridgeReqId != NULL_KEY) SayState(BRIDGE_TIMEOUT_MSG ) ;
  449. else if (llGetListLength(Rooms) == 0 ) SayState(NO_ROOMS_MSG ) ;
  450. else if (ChatListener != 0 ) SayState(BRIDGE_SELECT_TIMEOUT) ;
  451. llListenRemove(ChatListener) ; ChatListener = 0 ;
  452. if (ShouldResume) state default ;
  453. else state ready ;
  454. }
  455. }