Camamba Chat Helpers

decorates "knownUsers" and "rooms" objects with functions useful for console and other scripts

当前为 2021-03-27 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/423722/915506/Camamba%20Chat%20Helpers.js

  1. // ==UserScript==
  2. // @name Camamba Chat Helpers
  3. // @namespace dannysaurus.camamba
  4. // @version 0.1.6
  5. // @description decorates "knownUsers" and "rooms" objects with functions useful for console and other scripts
  6. // @license MIT License
  7. // @include https://www.camamba.com/chat/
  8. // @include https://www.de.camamba.com/chat/
  9. // @include https://www.camamba.com/chat/
  10. // @include https://www.de.camamba.com/chat/
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. /* jslint esversion: 9 */
  15. /* global me, camData, rooms, blockList, friendList, friendRequests, adminMessages, jsLang, byId, myRooms, knownUsers, activeRoom, selectedUser, settings, onMessageHandlers, postMessageHandlers, janusSend, wsSend, activeMainRoom, postToSite */
  16.  
  17. (function() {
  18. function decorateUsers(users = {}) {
  19.  
  20. const isUser = (user) => {
  21. return user.id;
  22. };
  23.  
  24. const toArray = () => {
  25. if (Array.isArray(users)) {
  26. return [...users];
  27. }
  28.  
  29. if (users.id && users.name) {
  30. return [ users ];
  31. }
  32.  
  33. return Object.values(users);
  34. };
  35.  
  36. const toString = () => {
  37. return toArray().map(u => {
  38.  
  39. return Object.entries(u)
  40. .map(([prop, val]) => prop + ':' + val)
  41. .join('\t');
  42.  
  43. }).join('\n');
  44. };
  45.  
  46. const by = (userPredicateFnc) => {
  47. const result = [], excluded = [];
  48.  
  49. Object.values(users).filter(u => isUser(u)).forEach(u => {
  50. if(userPredicateFnc(u)) {
  51. result.push(u);
  52. } else {
  53. excluded.push(u);
  54. }
  55. });
  56.  
  57. if (excluded.length) {
  58. result.excluded = decorateUsers(excluded);
  59. result.excludedAll = decorateUsers([ ...excluded, ...users.excludedAll ]);
  60. }
  61.  
  62. return decorateUsers(result);
  63. };
  64.  
  65. const byId = (id) => {
  66. return by(user => user.id == id);
  67. };
  68.  
  69. const byName = (name) => {
  70. const nameLower = String(name).toLowerCase();
  71. return by(user => {
  72. return user.name.toLowerCase().includes(nameLower);
  73. });
  74. };
  75.  
  76. const byGender = (gender) => {
  77. const genderLower = String(gender).toLowerCase();
  78. return by(user => {
  79. return user.gender.toLowerCase().startsWIth(genderLower);
  80. });
  81. };
  82.  
  83. const bySelected = () => {
  84. const selectedUserId = selectedUser ? selectedUser.dataset.id : 0;
  85.  
  86. if (!selectedUserId) {
  87. return by(user => false);
  88. }
  89.  
  90. return byId(selectedUserId);
  91. };
  92.  
  93. const byIsCammed = () => {
  94. if (!camData) return false;
  95.  
  96. const camDataUserIds = new Set(
  97. Object.values(camData)
  98. .filter(cd => cd.user)
  99. .map(cd => String(cd.user))
  100. );
  101.  
  102. return by(user => {
  103. return camDataUserIds.has(String(user.id));
  104. });
  105. };
  106.  
  107. const byViewing = () => {
  108. return users.by(user => {
  109. return user.viewing;
  110. });
  111. };
  112.  
  113. const byPos = (pos) => {
  114. return toArray()[pos];
  115. };
  116.  
  117. const stopViewing = () => {
  118. return byViewing().forEach(user => {
  119. janusSend('remove', user.id);
  120. });
  121. };
  122.  
  123. const save = () => toArray().forEach(user => {
  124. user.original = {...user};
  125. });
  126.  
  127. const restore = () => by(user => user.original).forEach(user => {
  128. Object.assign(user, user.original);
  129. delete user.original;
  130. });
  131.  
  132. const ban = (text, time, config = { isPublic: false, isPerma: false, suppressBanLog: false }) => {
  133. const { isPublic, isPerma, suppressBanLog } = config;
  134.  
  135. if (me.admin && toArray(users).length === 1) {
  136. const currentAdminTarget = users[0].id;
  137. // ban
  138. wsSend( { command: "ban", target: currentAdminTarget, reason: text, time: time * 3600 } );
  139. // notify user
  140. wsSend( { command: "admin", type: "admin", msg: { text: knownUsers[me.id].name + " banned " + knownUsers[currentAdminTarget].name+" for "+time+" hours.", room: activeMainRoom, notify: true }} );
  141. // to banlog
  142. if (!suppressBanLog) {
  143. postToSite("/adm_banned.php", "duration="+time+"&myname="+escape(knownUsers[me.id].name)+"&banname="+escape(knownUsers[currentAdminTarget].name)+"&roomname="+escape(activeMainRoom)+"&reason="+escape(text));
  144. }
  145. // to chat
  146. if (isPublic) {
  147. wsSend( { command: "admin", type: "room", msg: { text: knownUsers[currentAdminTarget].name+" has been banned.", room: activeMainRoom }} );
  148. }
  149. // do perma
  150. if (isPerma) {
  151. postToSite("/adm_set.php", "uID="+currentAdminTarget+"&ban=-1");
  152. }
  153. }
  154. };
  155.  
  156. return Object.defineProperties(users, Object.fromEntries(Object.entries({
  157. excluded: users.excluded || [],
  158. excludedAll: users.excludedAll || [],
  159. toArray,
  160. toString,
  161.  
  162. by,
  163.  
  164. byId,
  165. bySelected,
  166. byName,
  167. byGender,
  168. byPos,
  169. byIsCammed,
  170. byIsNotCammed: () => byIsCammed().excluded,
  171. byViewing,
  172.  
  173. stopViewing,
  174.  
  175. ban,
  176. banPerma: (text, time) => ban(text, time, { isPublic: false, isPerma: true, suppressBanLog: false }),
  177.  
  178. banSilent: (text, time) => ban(text, time, { isPublic: false, isPerma: false, suppressBanLog: true }),
  179. banSilentPerma: (text, time) => ban(text, time, { isPublic: false, isPerma: true, suppressBanLog: true }),
  180.  
  181. save,
  182. restore
  183. }).map(([propName, value]) => {
  184. return [propName, { value, configurable: true }];
  185. })));
  186. }
  187.  
  188. function decorateRooms(rooms = {}) {
  189. const roomsByName = (name) => {
  190. const nameLower = String(name).toLowerCase();
  191.  
  192. const result = {};
  193.  
  194. Object.entries(rooms).forEach(([roomId, roomName]) => {
  195.  
  196. if (roomName.toLowerCase().includes(nameLower)) {
  197. result[roomId] = roomName;
  198. }
  199. });
  200.  
  201. return result;
  202. };
  203.  
  204. return Object.defineProperties(rooms, {
  205. byName: { value: roomsByName, configurable: true },
  206. });
  207. }
  208.  
  209. const patchObject = ({ getExpected, doPatch, confirmAvailable = null, timeOutRetryMillis = 200, maxPeriodTryMillis = 5000 }) => {
  210. const expected = getExpected();
  211.  
  212. if (confirmAvailable && confirmAvailable(expected) || expected) {
  213.  
  214. if (timeOutRetryMillis <= maxPeriodTryMillis) {
  215.  
  216. setTimeout(() => {
  217. maxPeriodTryMillis -= timeOutRetryMillis;
  218. patchObject(getExpected, doPatch, confirmAvailable, timeOutRetryMillis, maxPeriodTryMillis);
  219.  
  220. }, timeOutRetryMillis);
  221. }
  222.  
  223. return;
  224. }
  225.  
  226. doPatch(expected);
  227. };
  228.  
  229. patchObject({
  230. getExpected: () => knownUsers,
  231. doPatch: (users) => {
  232. decorateUsers(users);
  233. }
  234. });
  235.  
  236. patchObject({
  237. getExpected: () => rooms,
  238. doPatch: (rooms) => {
  239. decorateRooms(rooms);
  240. }
  241. });
  242. })();