IdlePixel Group Chat

A private group chat panel

  1. // ==UserScript==
  2. // @name IdlePixel Group Chat
  3. // @namespace luxferre.dev
  4. // @version 2.1.2
  5. // @description A private group chat panel
  6. // @author Lux-Ferre
  7. // @license MIT
  8. // @match *://idle-pixel.com/login/play*
  9. // @grant none
  10. // @require https://gf.qytechs.cn/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905
  11. // @require https://gf.qytechs.cn/scripts/484046/code/IdlePixel%2B%20Custom%20Handling.js?anticache=20240721
  12. // @require https://gf.qytechs.cn/scripts/491983-idlepixel-plugin-paneller/code/IdlePixel%2B%20Plugin%20Paneller.js?anticache=20240721
  13. // ==/UserScript==
  14. (function() {
  15. 'use strict';
  16.  
  17. class GroupChatPlugin extends IdlePixelPlusPlugin {
  18. constructor() {
  19. super("groupChat", {
  20. about: {
  21. name: GM_info.script.name,
  22. version: GM_info.script.version,
  23. author: GM_info.script.author,
  24. description: GM_info.script.description
  25. },
  26. });
  27. this.groups = {}
  28. this.activeGroup = undefined
  29. this.invitations = {}
  30. this.chats = {}
  31. }
  32.  
  33. saveData(){
  34. const user = window["var_username"]
  35. const groupsJSON = JSON.stringify(this.groups)
  36. localStorage.setItem(`groupChatGroups${user}`, groupsJSON)
  37.  
  38. const invitationsJSON = JSON.stringify(this.invitations)
  39. localStorage.setItem(`groupChatInvitations${user}`, invitationsJSON)
  40. }
  41.  
  42. loadData(){
  43. const user = window["var_username"]
  44. const groupData = localStorage.getItem(`groupChatGroups${user}`)
  45. if (groupData){
  46. this.groups = JSON.parse(groupData)
  47. } else {
  48. this.groups = {}
  49. }
  50.  
  51. const invitationData = localStorage.getItem(`groupChatInvitations${user}`)
  52. if (invitationData){
  53. this.invitations = JSON.parse(invitationData)
  54. } else {
  55. this.invitations = {}
  56. }
  57. }
  58. createPanel(){
  59. IdlePixelPlus.addPanel("groupchat", "Group Chat Panel", function() {
  60. return `
  61. <div class="groupChatUIContainer w-100">
  62. <div id="groupChatInfoModule" class="row groupChatUIModule">
  63. <div class="col">
  64. <div class="row">
  65. <div class="col-4 text-end align-self-center groupChatInfoContainer">
  66. <div class="row gx-0">
  67. <div id="groupChatGroupNotification" class="col-1 text-center align-self-center groupChatGroupNotificationInactive" onclick="IdlePixelPlus.plugins.groupChat.showInvitationModal()"><span>!</span></div>
  68. <div class="col-11 align-self-center"><select id="groupChatGroupSelector" class="w-100"></select></div>
  69. </div>
  70. </div>
  71. <div class="col-8 d-flex groupChatInfoContainer">
  72. <div id="groupChatMembersContainer" class="d-flex align-items-center"><span>Members:</span></div>
  73. </div>
  74. </div>
  75. </div>
  76. </div>
  77. <div id="groupChatChatModule" class="row groupChatUIModule">
  78. <div class="col">
  79. <div class="row">
  80. <div id="groupChatChatFormContainer" class="col">
  81. <div id="groupChatChatBox" class="overflow-auto"></div>
  82. <form onsubmit="event.preventDefault(); IdlePixelPlus.plugins.groupChat.sendGroupChatButton();">
  83. <div class="row d-flex flex-fill">
  84. <div class="col-11"><input id="groupChatChatIn" class="form-control w-100" type="text" /></div>
  85. <div class="col-1"><input id="groupChatChatButton" class="w-100 h-100" type="submit" value="Send" /></div>
  86. </div>
  87. </form>
  88. </div>
  89. </div>
  90. </div>
  91. </div>
  92. <div id="groupChatSettingsModules" class="row groupChatUIModule">
  93. <div class="col">
  94. <div class="row">
  95. <div id="groupChatSettings" class="col d-flex justify-content-around align-self-center"><button id="groupChatCreateGroupButton" class="btn btn-info" type="button" onclick="IdlePixelPlus.plugins.groupChat.createGroupButton()">Create Group</button><button id="groupChatShareRaidButton" class="btn btn-info groupChatInGroupButton" type="button" onclick="IdlePixelPlus.plugins.groupChat.shareRaidButton()">Share Raid</button><button id="groupChatInviteButton" class="btn btn-info groupChatOwnerButton groupChatInGroupButton" type="button" onclick="IdlePixelPlus.plugins.groupChat.inviteButton()">Invite</button><button id="groupChatUninviteButton" class="btn btn-info groupChatOwnerButton groupChatInGroupButton" type="button" onclick="IdlePixelPlus.plugins.groupChat.uninviteButton()">Uninvite</button><button id="groupChatRemovePlayerButton" class="btn btn-info disabled groupChatOwnerButton groupChatInGroupButton" type="button" disabled>Remove</button></div>
  96. </div>
  97. </div>
  98. </div>
  99. </div>
  100. `
  101. });
  102. }
  103.  
  104. addStyles(){
  105. let backgroundColour
  106. let textColour
  107.  
  108. if ("ui-tweaks" in IdlePixelPlus.plugins){
  109. backgroundColour = IdlePixelPlus.plugins["ui-tweaks"].config["color-chat-area"]
  110. textColour = IdlePixelPlus.plugins["ui-tweaks"].config["font-color-chat-area"]
  111. } else {
  112. backgroundColour = "white"
  113. textColour = "black"
  114. }
  115.  
  116. $("head").append(`
  117. <style id="styles-groupchat">
  118. #groupChatInvitationsModalInner {
  119. background-color: ${backgroundColour};
  120. color: ${textColour};
  121. }
  122.  
  123. .groupChatUIModule {
  124. border: outset 2px;
  125. }
  126.  
  127. .groupChatUIContainer {
  128. width: 100%;
  129. height: 100%;
  130. padding: 5px;
  131. margin: 0;
  132. }
  133.  
  134. #groupChatChatBox {
  135. width: 100%;
  136. height: 70vh;
  137. margin-top: 10px;
  138. }
  139.  
  140. #groupChatChatBox {
  141. border: inset 1px;
  142. }
  143.  
  144. .groupChatGroupNotificationActive {
  145. color: red;
  146. border: outset;
  147. border-color: red;
  148. }
  149.  
  150. .groupChatGroupNotificationInactive {
  151. color: gray;
  152. border: outset;
  153. border-color: gray;
  154. }
  155.  
  156. .groupChatInfoContainer {
  157. border: inset;
  158. }
  159.  
  160. #groupChatSelectGroupDrop {
  161. border: solid;
  162. border-width: 1px;
  163. }
  164.  
  165. .groupChatMember {
  166. margin: 0px 3px;
  167. border: 1px groove;
  168. }
  169.  
  170. #groupChatGroupNotification {
  171. cursor: pointer;
  172. }
  173.  
  174. .groupChatInvitation {
  175. background-color: RGBA(1, 150, 150, 0.5);
  176. margin-bottom: 2px;
  177. }
  178.  
  179. .groupChatInvCheck {
  180. cursor: pointer;
  181. }
  182.  
  183. .groupChatInvCross {
  184. cursor: pointer;
  185. margin-right: 5px;
  186. }
  187.  
  188. .groupChatInvData {
  189. margin-left: 10px;
  190. }
  191.  
  192. #groupChatModalHeader {
  193. padding: calc(var(--bs-modal-padding) - var(--bs-modal-header-gap) * .5);
  194. background-color: var(--bs-modal-header-bg);
  195. border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);
  196. border-top-right-radius: var(--bs-modal-inner-border-radius);
  197. border-top-left-radius: var(--bs-modal-inner-border-radius);
  198. }
  199.  
  200. #groupChatInvitationsModal .modal-body {
  201. overflow-y: auto;
  202. }
  203. </style>
  204. `)
  205. }
  206. onLogin() {
  207. Paneller.registerPanel("groupchat", "Group Chat")
  208.  
  209. this.loadData()
  210. this.createPanel()
  211. this.addStyles()
  212. this.createNotification()
  213. this.updatePanelInfo()
  214. this.createInvModal()
  215. this.updateInvitationNotification()
  216.  
  217. $('#groupChatGroupSelector').on('change', function() {
  218. IdlePixelPlus.plugins.groupChat.joinGroup(this.value)
  219. });
  220.  
  221. $("#groupChatChatModule").hide()
  222. $(".groupChatInGroupButton").hide()
  223. }
  224.  
  225. onPanelChanged(panelBefore, panelAfter){
  226. if (panelAfter==="groupchat"){
  227. $("#groupChatNotification").hide()
  228. }
  229. }
  230. onConfigsChanged() {
  231. this.updatePanelInfo();
  232. }
  233.  
  234. createInvModal(){
  235. const modalString = `
  236. <div id="groupChatInvitationsModal" class="modal fade" role="dialog" tabindex="-1"">
  237. <div class="modal-dialog modal-dialog-centered" role="document">
  238. <div id="groupChatInvitationsModalInner" class="modal-content">
  239. <div id="groupChatModalHeader" class="modal-header text-center">
  240. <h3 class="modal-title w-100"><u>Pending Invitations</u></h3>
  241. </div>
  242. <div class="modal-body">
  243. <div id="groupChatInvitationsModalList" class="overflow-auto"></div>
  244. </div>
  245. </div>
  246. </div>
  247. </div>
  248. `
  249.  
  250. const modalElement = $.parseHTML(modalString)
  251. $(document.body).append(modalElement)
  252. }
  253.  
  254. addInvitationsToModal(){
  255. const invitationsModalList = $("#groupChatInvitationsModalList")
  256. invitationsModalList.empty()
  257.  
  258. for (const [groupName, inviter] of Object.entries(this.invitations)){
  259. const newItemString = `<div class="d-flex justify-content-between rounded-pill groupChatInvitation"><span class="groupChatInvData">${groupName} | ${inviter}</span><div><span data-groupName="${groupName}" class="groupChatInvCheck">✔️</span> | <span data-groupName="${groupName}" class="groupChatInvCross">❌</span></div></div>`
  260. const newItemElement = $.parseHTML(newItemString)
  261. invitationsModalList.append(newItemElement)
  262. }
  263.  
  264. $(".groupChatInvCheck").attr("onclick", "IdlePixelPlus.plugins.groupChat.acceptInvitation(this.getAttribute('data-groupName'))")
  265. $(".groupChatInvCross").attr("onclick", "IdlePixelPlus.plugins.groupChat.rejectInvitation(this.getAttribute('data-groupName'))")
  266. }
  267.  
  268. showInvitationModal(){
  269. document.body.scrollTop = document.documentElement.scrollTop = 0;
  270.  
  271. this.addInvitationsToModal()
  272.  
  273. $('#groupChatInvitationsModal').modal('show')
  274. }
  275. onCustomMessageReceived(player, content, callbackId) {
  276. const customData = Customs.parseCustom(player, content, callbackId)
  277. if(customData.plugin==="groupchat") {
  278. if(customData.command==="chat"){
  279. this.handleReceivedChat(customData)
  280. } else if (customData.command==="invite"){
  281. this.onInviteReceived(customData.player, customData.payload, customData.callbackId)
  282. } else if (customData.command==="accept"){
  283. this.onAcceptedInvite(customData.player, customData.payload)
  284. } else if (customData.command==="reject"){
  285. this.onRejectedInvite(customData.player, customData.payload)
  286. } else if (customData.command==="unaccept"){
  287. this.onUnacceptInvite(customData.player, customData.payload)
  288. } else if (customData.command==="join"){
  289. this.addGroup(customData.player, customData.payload)
  290. }
  291. }
  292. }
  293.  
  294. handleReceivedChat(data){
  295. const player = data.player
  296. const splitData = data.payload.split(";")
  297. const groupName = splitData[0]
  298. const givenPassword = splitData[1]
  299. const message = splitData.slice(2).join(";")
  300.  
  301. if(!(groupName in this.groups)){console.log(`Group ${groupName} doesn't exist.`); return}
  302.  
  303. const correctPassword = this.groups[groupName].password
  304. if(givenPassword!==correctPassword){console.log(`Incorrct password given`); return}
  305.  
  306. if (!(this.groups[groupName].members.includes(player))){
  307. this.addPlayerToGroup(player, groupName)
  308. }
  309.  
  310. const newMessageString = `<div class=""><span class="color-green">${Chat._get_time()}</span><span><strong>${player}: </strong></span><span>${sanitize_input(message)}</span></div>`
  311.  
  312. if(!(groupName in this.chats)){this.chats[groupName] = []}
  313.  
  314. this.chats[groupName].push(newMessageString)
  315.  
  316. if(this.activeGroup.name === groupName){
  317. this.addMessageToChat(newMessageString)
  318. }
  319. }
  320. updatePanelInfo(){
  321. this.updateGroupsList()
  322. }
  323. createNotification(){
  324. const notificationString = `
  325. <div id="groupChatNotification" class="notification hover" onclick="IdlePixelPlus.setPanel('groupchat')">
  326. <img src="https://d1xsc8x7nc5q8t.cloudfront.net/images/meteor_radar_detector.png" class="w20" alt="">
  327. <span class="font-small color-yellow">Group Chat</span>
  328. </div>
  329. `
  330.  
  331. const notificationElement = $.parseHTML(notificationString)
  332. const notificationBar = $("#notifications-area")
  333.  
  334. notificationBar.append(notificationElement)
  335. $("#groupChatNotification").hide()
  336. }
  337.  
  338. showNotification(){
  339. if(Globals.currentPanel === "panel-groupchat"){return;}
  340. $("#groupChatNotification").show()
  341. }
  342.  
  343. sendGroupChat(chatMessage){
  344. if(!this.activeGroup){console.log("No active Group!"); return}
  345. const password = this.activeGroup.password
  346. const memberList = this.activeGroup.members
  347. const groupName = this.activeGroup.name
  348.  
  349. memberList.forEach(member => {
  350. Customs.sendBasicCustom(member, "groupchat", "chat", `${groupName};${password};${chatMessage}`)
  351. })
  352. }
  353.  
  354. sendGroupChatButton(){
  355. const chatIn = $("#groupChatChatIn")
  356. const chatMessage = chatIn.val()
  357. chatIn.val("")
  358. this.sendGroupChat(chatMessage)
  359. }
  360.  
  361. addMessageToChat(messageText){
  362. const chatBox = $("#groupChatChatBox")
  363.  
  364. const messageElement = $.parseHTML(messageText)
  365. chatBox.append(messageElement);
  366.  
  367. chatBox.scrollTop(chatBox[0].scrollHeight);
  368.  
  369. this.showNotification()
  370. }
  371.  
  372. genPass(){
  373. return Math.random().toString(36).slice(2)
  374. }
  375.  
  376. createGroup(groupName){
  377. if (groupName in this.groups){
  378. console.log("Group already exists")
  379. return
  380. }
  381.  
  382. this.groups[groupName] = {
  383. name: groupName,
  384. members: [window["var_username"]],
  385. invited_members: [],
  386. password: this.genPass(),
  387. owner: window["var_username"]
  388. }
  389.  
  390. this.updateGroupsList()
  391. this.saveData()
  392. }
  393.  
  394. joinGroup(group_name){
  395. const loadedGroup = this.groups[group_name]
  396. this.activeGroup = {
  397. name: group_name,
  398. members: loadedGroup.members,
  399. password: loadedGroup.password,
  400. owner: loadedGroup.owner,
  401. online_members: new Set(),
  402. }
  403.  
  404. this.updateMembersList()
  405.  
  406. $("#groupChatChatModule").show()
  407. $(".groupChatInGroupButton").show()
  408.  
  409. const ownerButtons = $(".groupChatOwnerButton")
  410.  
  411. if (this.activeGroup.owner === window["var_username"]){
  412. ownerButtons.show()
  413. } else {
  414. ownerButtons.hide()
  415. }
  416.  
  417. const chatBox = $("#groupChatChatBox")
  418.  
  419. chatBox.empty()
  420.  
  421. if(!(group_name in this.chats)){return}
  422.  
  423. this.chats[group_name].forEach(chatMessage=>{
  424. this.addMessageToChat(chatMessage)
  425. })
  426.  
  427. chatBox.scrollTop(chatBox[0].scrollHeight);
  428. }
  429.  
  430. addPlayerToGroup(player, group){
  431. if (this.groups[group].members.includes(player)){return}
  432. this.groups[group].members.push(player)
  433.  
  434. this.updateMembersList()
  435. this.saveData()
  436. }
  437.  
  438. getGroups(){
  439. return Object.keys(this.groups)
  440. }
  441.  
  442. updateGroupsList(){
  443. const groups = this.getGroups()
  444. const groupSelector = $("#groupChatGroupSelector")
  445. groupSelector.empty()
  446.  
  447. groupSelector.append(`<option disabled selected value=""> -- select a group -- </option>`)
  448.  
  449. groups.forEach(groupName=>{
  450. groupSelector.append(`<option value="${groupName}">${groupName}</option>`)
  451. })
  452. }
  453.  
  454. updateMembersList(){
  455. const membersDisplay = $("#groupChatMembersContainer")
  456. membersDisplay.empty()
  457. membersDisplay.append(`<span>Members: </span>`)
  458. this.activeGroup.members.forEach(member=>{
  459. membersDisplay.append(`<span class="rounded-pill groupChatMember">&nbsp ${member} &nbsp</span>`)
  460. })
  461. }
  462.  
  463. addPlayertoInvited(player, group){
  464. this.groups[group].invited_members.push(player)
  465. this.saveData()
  466. }
  467.  
  468. sendInvitation(player){
  469. if(!this.activeGroup){return}
  470. if(this.activeGroup.owner !== window["var_username"]){
  471. console.log("Only the owner can invite players.")
  472. return
  473. }
  474. if(this.activeGroup.members.length >7){
  475. console.log("Group too big to invite more players!")
  476. return
  477. }
  478.  
  479. IdlePixelPlus.sendCustomMessage(player, {
  480. content: `groupchat:invite:${this.activeGroup.name}`,
  481. onResponse: function(player, content, callbackId) {
  482. IdlePixelPlus.plugins.groupChat.addPlayertoInvited(player, content)
  483. console.log("Player invited!")
  484. return true;
  485. },
  486. onOffline: function(player, content) {
  487. console.log("Cannot invite offline player!")
  488. return true;
  489. },
  490. timeout: 2000 // callback expires after 2 seconds
  491. });
  492.  
  493. }
  494.  
  495. onInviteReceived(player, groupName, callback){
  496. this.invitations[groupName] = player
  497. this.updateInvitationNotification()
  498. IdlePixelPlus.sendCustomMessage(player, {
  499. content: `${groupName}`,
  500. callbackId: callback,
  501. onResponse: function(player, content, callbackId) {return true;},
  502. onOffline: function(player, content) {return true;},
  503. timeout: 2000 // callback expires after 2 seconds
  504. });
  505. this.saveData()
  506. }
  507.  
  508. acceptInvitation(groupName){
  509. Customs.sendBasicCustom(this.invitations[groupName], "groupchat", "accept", groupName)
  510. delete this.invitations[groupName]
  511. this.addInvitationsToModal()
  512. this.updateInvitationNotification()
  513. this.saveData()
  514. }
  515.  
  516. rejectInvitation(groupName){
  517. Customs.sendBasicCustom(this.invitations[groupName], "groupchat", "reject", `${groupName};Player rejected invitation.`)
  518. delete this.invitations[groupName]
  519. this.addInvitationsToModal()
  520. this.updateInvitationNotification()
  521. this.saveData()
  522. }
  523.  
  524. onAcceptedInvite(player, groupName){
  525. if(!(groupName in this.groups)){return}
  526. if (this.groups[groupName].members.length >= 8){
  527. console.log("Group too big!")
  528. Customs.sendBasicCustom(player, "groupchat", "unaccept", "Group has too many members")
  529. return
  530. }
  531.  
  532. if(!this.groups[groupName].invited_members.includes(player)){console.log("Univited player"); return;}
  533.  
  534. this.groups[groupName].invited_members = this.groups[groupName].invited_members.filter(item => item !== player)
  535. this.addPlayerToGroup(player, groupName)
  536.  
  537. let data_string = `${groupName};${this.groups[groupName]["password"]};`
  538.  
  539. let membersString = ""
  540. this.groups[groupName].members.forEach(member=>{
  541. membersString+=`,${member}`
  542. })
  543. membersString = membersString.slice(1)
  544. data_string += membersString
  545. Customs.sendBasicCustom(player, "groupchat", "join", data_string)
  546. this.saveData()
  547. }
  548.  
  549. onRejectedInvite(player, data){
  550. const splitData = data.split(";")
  551. const groupName = splitData[0]
  552. const reason = splitData.slice(1).join(";")
  553. this.groups[groupName].invited_members = this.groups[groupName].invited_members.filter(item => item !== player)
  554. console.log(`${player} could not be added to ${groupName} for reason: ${reason}`)
  555. this.saveData()
  556. }
  557.  
  558. onUnacceptInvite(player, data){
  559. const splitData = data.split(";")
  560. const groupName = splitData[0]
  561. const reason = splitData.slice(1).join(";")
  562. console.log(`Could not be added to ${groupName} for reason: ${reason}`)
  563. }
  564.  
  565. createGroupButton(){
  566. const newGroup = prompt("Group Name:")
  567. this.createGroup(newGroup)
  568. }
  569.  
  570. shareRaidButton(){
  571. const raidCode = $("#raids-team-panel-uuid").text()
  572. if (raidCode===""){return}
  573. this.sendGroupChat(raidCode)
  574. }
  575.  
  576. inviteButton(){
  577. const newPlayer = prompt("Player Name:")
  578. this.sendInvitation(newPlayer)
  579. }
  580.  
  581. uninviteButton(){
  582. const player = prompt("Player Name:")
  583. const groupName = this.activeGroup.name
  584.  
  585. this.groups[groupName].invited_members = this.groups[groupName].invited_members.filter(item => item !== player)
  586. this.saveData()
  587. }
  588.  
  589. addGroup(player, data){
  590. const splitData = data.split(";")
  591. const groupName = splitData[0]
  592. const password = splitData[1]
  593. const membersList = splitData[2].split(",")
  594.  
  595. if (groupName in this.groups){
  596. console.log("Group already exists")
  597. return
  598. }
  599.  
  600. this.groups[groupName] = {
  601. name: groupName,
  602. members: membersList,
  603. invited_members: [],
  604. password: password,
  605. owner: player
  606. }
  607.  
  608. this.updateGroupsList()
  609. this.saveData()
  610. }
  611.  
  612. updateInvitationNotification(){
  613. const notificationButton = $("#groupChatGroupNotification")
  614. if (Object.keys(this.invitations).length){
  615. notificationButton.removeClass("groupChatGroupNotificationInactive")
  616. notificationButton.addClass("groupChatGroupNotificationActive")
  617. } else {
  618. notificationButton.removeClass("groupChatGroupNotificationActive")
  619. notificationButton.addClass("groupChatGroupNotificationInactive")
  620. }
  621. }
  622. }
  623.  
  624. const plugin = new GroupChatPlugin();
  625. IdlePixelPlus.registerPlugin(plugin);
  626. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址