您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A private group chat panel
// ==UserScript== // @name IdlePixel Group Chat // @namespace luxferre.dev // @version 2.1.2 // @description A private group chat panel // @author Lux-Ferre // @license MIT // @match *://idle-pixel.com/login/play* // @grant none // @require https://greasyfork.org/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905 // @require https://greasyfork.org/scripts/484046/code/IdlePixel%2B%20Custom%20Handling.js?anticache=20240721 // @require https://greasyfork.org/scripts/491983-idlepixel-plugin-paneller/code/IdlePixel%2B%20Plugin%20Paneller.js?anticache=20240721 // ==/UserScript== (function() { 'use strict'; class GroupChatPlugin extends IdlePixelPlusPlugin { constructor() { super("groupChat", { about: { name: GM_info.script.name, version: GM_info.script.version, author: GM_info.script.author, description: GM_info.script.description }, }); this.groups = {} this.activeGroup = undefined this.invitations = {} this.chats = {} } saveData(){ const user = window["var_username"] const groupsJSON = JSON.stringify(this.groups) localStorage.setItem(`groupChatGroups${user}`, groupsJSON) const invitationsJSON = JSON.stringify(this.invitations) localStorage.setItem(`groupChatInvitations${user}`, invitationsJSON) } loadData(){ const user = window["var_username"] const groupData = localStorage.getItem(`groupChatGroups${user}`) if (groupData){ this.groups = JSON.parse(groupData) } else { this.groups = {} } const invitationData = localStorage.getItem(`groupChatInvitations${user}`) if (invitationData){ this.invitations = JSON.parse(invitationData) } else { this.invitations = {} } } createPanel(){ IdlePixelPlus.addPanel("groupchat", "Group Chat Panel", function() { return ` <div class="groupChatUIContainer w-100"> <div id="groupChatInfoModule" class="row groupChatUIModule"> <div class="col"> <div class="row"> <div class="col-4 text-end align-self-center groupChatInfoContainer"> <div class="row gx-0"> <div id="groupChatGroupNotification" class="col-1 text-center align-self-center groupChatGroupNotificationInactive" onclick="IdlePixelPlus.plugins.groupChat.showInvitationModal()"><span>!</span></div> <div class="col-11 align-self-center"><select id="groupChatGroupSelector" class="w-100"></select></div> </div> </div> <div class="col-8 d-flex groupChatInfoContainer"> <div id="groupChatMembersContainer" class="d-flex align-items-center"><span>Members:</span></div> </div> </div> </div> </div> <div id="groupChatChatModule" class="row groupChatUIModule"> <div class="col"> <div class="row"> <div id="groupChatChatFormContainer" class="col"> <div id="groupChatChatBox" class="overflow-auto"></div> <form onsubmit="event.preventDefault(); IdlePixelPlus.plugins.groupChat.sendGroupChatButton();"> <div class="row d-flex flex-fill"> <div class="col-11"><input id="groupChatChatIn" class="form-control w-100" type="text" /></div> <div class="col-1"><input id="groupChatChatButton" class="w-100 h-100" type="submit" value="Send" /></div> </div> </form> </div> </div> </div> </div> <div id="groupChatSettingsModules" class="row groupChatUIModule"> <div class="col"> <div class="row"> <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> </div> </div> </div> </div> ` }); } addStyles(){ let backgroundColour let textColour if ("ui-tweaks" in IdlePixelPlus.plugins){ backgroundColour = IdlePixelPlus.plugins["ui-tweaks"].config["color-chat-area"] textColour = IdlePixelPlus.plugins["ui-tweaks"].config["font-color-chat-area"] } else { backgroundColour = "white" textColour = "black" } $("head").append(` <style id="styles-groupchat"> #groupChatInvitationsModalInner { background-color: ${backgroundColour}; color: ${textColour}; } .groupChatUIModule { border: outset 2px; } .groupChatUIContainer { width: 100%; height: 100%; padding: 5px; margin: 0; } #groupChatChatBox { width: 100%; height: 70vh; margin-top: 10px; } #groupChatChatBox { border: inset 1px; } .groupChatGroupNotificationActive { color: red; border: outset; border-color: red; } .groupChatGroupNotificationInactive { color: gray; border: outset; border-color: gray; } .groupChatInfoContainer { border: inset; } #groupChatSelectGroupDrop { border: solid; border-width: 1px; } .groupChatMember { margin: 0px 3px; border: 1px groove; } #groupChatGroupNotification { cursor: pointer; } .groupChatInvitation { background-color: RGBA(1, 150, 150, 0.5); margin-bottom: 2px; } .groupChatInvCheck { cursor: pointer; } .groupChatInvCross { cursor: pointer; margin-right: 5px; } .groupChatInvData { margin-left: 10px; } #groupChatModalHeader { padding: calc(var(--bs-modal-padding) - var(--bs-modal-header-gap) * .5); background-color: var(--bs-modal-header-bg); border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); border-top-right-radius: var(--bs-modal-inner-border-radius); border-top-left-radius: var(--bs-modal-inner-border-radius); } #groupChatInvitationsModal .modal-body { overflow-y: auto; } </style> `) } onLogin() { Paneller.registerPanel("groupchat", "Group Chat") this.loadData() this.createPanel() this.addStyles() this.createNotification() this.updatePanelInfo() this.createInvModal() this.updateInvitationNotification() $('#groupChatGroupSelector').on('change', function() { IdlePixelPlus.plugins.groupChat.joinGroup(this.value) }); $("#groupChatChatModule").hide() $(".groupChatInGroupButton").hide() } onPanelChanged(panelBefore, panelAfter){ if (panelAfter==="groupchat"){ $("#groupChatNotification").hide() } } onConfigsChanged() { this.updatePanelInfo(); } createInvModal(){ const modalString = ` <div id="groupChatInvitationsModal" class="modal fade" role="dialog" tabindex="-1""> <div class="modal-dialog modal-dialog-centered" role="document"> <div id="groupChatInvitationsModalInner" class="modal-content"> <div id="groupChatModalHeader" class="modal-header text-center"> <h3 class="modal-title w-100"><u>Pending Invitations</u></h3> </div> <div class="modal-body"> <div id="groupChatInvitationsModalList" class="overflow-auto"></div> </div> </div> </div> </div> ` const modalElement = $.parseHTML(modalString) $(document.body).append(modalElement) } addInvitationsToModal(){ const invitationsModalList = $("#groupChatInvitationsModalList") invitationsModalList.empty() for (const [groupName, inviter] of Object.entries(this.invitations)){ 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>` const newItemElement = $.parseHTML(newItemString) invitationsModalList.append(newItemElement) } $(".groupChatInvCheck").attr("onclick", "IdlePixelPlus.plugins.groupChat.acceptInvitation(this.getAttribute('data-groupName'))") $(".groupChatInvCross").attr("onclick", "IdlePixelPlus.plugins.groupChat.rejectInvitation(this.getAttribute('data-groupName'))") } showInvitationModal(){ document.body.scrollTop = document.documentElement.scrollTop = 0; this.addInvitationsToModal() $('#groupChatInvitationsModal').modal('show') } onCustomMessageReceived(player, content, callbackId) { const customData = Customs.parseCustom(player, content, callbackId) if(customData.plugin==="groupchat") { if(customData.command==="chat"){ this.handleReceivedChat(customData) } else if (customData.command==="invite"){ this.onInviteReceived(customData.player, customData.payload, customData.callbackId) } else if (customData.command==="accept"){ this.onAcceptedInvite(customData.player, customData.payload) } else if (customData.command==="reject"){ this.onRejectedInvite(customData.player, customData.payload) } else if (customData.command==="unaccept"){ this.onUnacceptInvite(customData.player, customData.payload) } else if (customData.command==="join"){ this.addGroup(customData.player, customData.payload) } } } handleReceivedChat(data){ const player = data.player const splitData = data.payload.split(";") const groupName = splitData[0] const givenPassword = splitData[1] const message = splitData.slice(2).join(";") if(!(groupName in this.groups)){console.log(`Group ${groupName} doesn't exist.`); return} const correctPassword = this.groups[groupName].password if(givenPassword!==correctPassword){console.log(`Incorrct password given`); return} if (!(this.groups[groupName].members.includes(player))){ this.addPlayerToGroup(player, groupName) } const newMessageString = `<div class=""><span class="color-green">${Chat._get_time()}</span><span><strong>${player}: </strong></span><span>${sanitize_input(message)}</span></div>` if(!(groupName in this.chats)){this.chats[groupName] = []} this.chats[groupName].push(newMessageString) if(this.activeGroup.name === groupName){ this.addMessageToChat(newMessageString) } } updatePanelInfo(){ this.updateGroupsList() } createNotification(){ const notificationString = ` <div id="groupChatNotification" class="notification hover" onclick="IdlePixelPlus.setPanel('groupchat')"> <img src="https://d1xsc8x7nc5q8t.cloudfront.net/images/meteor_radar_detector.png" class="w20" alt=""> <span class="font-small color-yellow">Group Chat</span> </div> ` const notificationElement = $.parseHTML(notificationString) const notificationBar = $("#notifications-area") notificationBar.append(notificationElement) $("#groupChatNotification").hide() } showNotification(){ if(Globals.currentPanel === "panel-groupchat"){return;} $("#groupChatNotification").show() } sendGroupChat(chatMessage){ if(!this.activeGroup){console.log("No active Group!"); return} const password = this.activeGroup.password const memberList = this.activeGroup.members const groupName = this.activeGroup.name memberList.forEach(member => { Customs.sendBasicCustom(member, "groupchat", "chat", `${groupName};${password};${chatMessage}`) }) } sendGroupChatButton(){ const chatIn = $("#groupChatChatIn") const chatMessage = chatIn.val() chatIn.val("") this.sendGroupChat(chatMessage) } addMessageToChat(messageText){ const chatBox = $("#groupChatChatBox") const messageElement = $.parseHTML(messageText) chatBox.append(messageElement); chatBox.scrollTop(chatBox[0].scrollHeight); this.showNotification() } genPass(){ return Math.random().toString(36).slice(2) } createGroup(groupName){ if (groupName in this.groups){ console.log("Group already exists") return } this.groups[groupName] = { name: groupName, members: [window["var_username"]], invited_members: [], password: this.genPass(), owner: window["var_username"] } this.updateGroupsList() this.saveData() } joinGroup(group_name){ const loadedGroup = this.groups[group_name] this.activeGroup = { name: group_name, members: loadedGroup.members, password: loadedGroup.password, owner: loadedGroup.owner, online_members: new Set(), } this.updateMembersList() $("#groupChatChatModule").show() $(".groupChatInGroupButton").show() const ownerButtons = $(".groupChatOwnerButton") if (this.activeGroup.owner === window["var_username"]){ ownerButtons.show() } else { ownerButtons.hide() } const chatBox = $("#groupChatChatBox") chatBox.empty() if(!(group_name in this.chats)){return} this.chats[group_name].forEach(chatMessage=>{ this.addMessageToChat(chatMessage) }) chatBox.scrollTop(chatBox[0].scrollHeight); } addPlayerToGroup(player, group){ if (this.groups[group].members.includes(player)){return} this.groups[group].members.push(player) this.updateMembersList() this.saveData() } getGroups(){ return Object.keys(this.groups) } updateGroupsList(){ const groups = this.getGroups() const groupSelector = $("#groupChatGroupSelector") groupSelector.empty() groupSelector.append(`<option disabled selected value=""> -- select a group -- </option>`) groups.forEach(groupName=>{ groupSelector.append(`<option value="${groupName}">${groupName}</option>`) }) } updateMembersList(){ const membersDisplay = $("#groupChatMembersContainer") membersDisplay.empty() membersDisplay.append(`<span>Members: </span>`) this.activeGroup.members.forEach(member=>{ membersDisplay.append(`<span class="rounded-pill groupChatMember">  ${member}  </span>`) }) } addPlayertoInvited(player, group){ this.groups[group].invited_members.push(player) this.saveData() } sendInvitation(player){ if(!this.activeGroup){return} if(this.activeGroup.owner !== window["var_username"]){ console.log("Only the owner can invite players.") return } if(this.activeGroup.members.length >7){ console.log("Group too big to invite more players!") return } IdlePixelPlus.sendCustomMessage(player, { content: `groupchat:invite:${this.activeGroup.name}`, onResponse: function(player, content, callbackId) { IdlePixelPlus.plugins.groupChat.addPlayertoInvited(player, content) console.log("Player invited!") return true; }, onOffline: function(player, content) { console.log("Cannot invite offline player!") return true; }, timeout: 2000 // callback expires after 2 seconds }); } onInviteReceived(player, groupName, callback){ this.invitations[groupName] = player this.updateInvitationNotification() IdlePixelPlus.sendCustomMessage(player, { content: `${groupName}`, callbackId: callback, onResponse: function(player, content, callbackId) {return true;}, onOffline: function(player, content) {return true;}, timeout: 2000 // callback expires after 2 seconds }); this.saveData() } acceptInvitation(groupName){ Customs.sendBasicCustom(this.invitations[groupName], "groupchat", "accept", groupName) delete this.invitations[groupName] this.addInvitationsToModal() this.updateInvitationNotification() this.saveData() } rejectInvitation(groupName){ Customs.sendBasicCustom(this.invitations[groupName], "groupchat", "reject", `${groupName};Player rejected invitation.`) delete this.invitations[groupName] this.addInvitationsToModal() this.updateInvitationNotification() this.saveData() } onAcceptedInvite(player, groupName){ if(!(groupName in this.groups)){return} if (this.groups[groupName].members.length >= 8){ console.log("Group too big!") Customs.sendBasicCustom(player, "groupchat", "unaccept", "Group has too many members") return } if(!this.groups[groupName].invited_members.includes(player)){console.log("Univited player"); return;} this.groups[groupName].invited_members = this.groups[groupName].invited_members.filter(item => item !== player) this.addPlayerToGroup(player, groupName) let data_string = `${groupName};${this.groups[groupName]["password"]};` let membersString = "" this.groups[groupName].members.forEach(member=>{ membersString+=`,${member}` }) membersString = membersString.slice(1) data_string += membersString Customs.sendBasicCustom(player, "groupchat", "join", data_string) this.saveData() } onRejectedInvite(player, data){ const splitData = data.split(";") const groupName = splitData[0] const reason = splitData.slice(1).join(";") this.groups[groupName].invited_members = this.groups[groupName].invited_members.filter(item => item !== player) console.log(`${player} could not be added to ${groupName} for reason: ${reason}`) this.saveData() } onUnacceptInvite(player, data){ const splitData = data.split(";") const groupName = splitData[0] const reason = splitData.slice(1).join(";") console.log(`Could not be added to ${groupName} for reason: ${reason}`) } createGroupButton(){ const newGroup = prompt("Group Name:") this.createGroup(newGroup) } shareRaidButton(){ const raidCode = $("#raids-team-panel-uuid").text() if (raidCode===""){return} this.sendGroupChat(raidCode) } inviteButton(){ const newPlayer = prompt("Player Name:") this.sendInvitation(newPlayer) } uninviteButton(){ const player = prompt("Player Name:") const groupName = this.activeGroup.name this.groups[groupName].invited_members = this.groups[groupName].invited_members.filter(item => item !== player) this.saveData() } addGroup(player, data){ const splitData = data.split(";") const groupName = splitData[0] const password = splitData[1] const membersList = splitData[2].split(",") if (groupName in this.groups){ console.log("Group already exists") return } this.groups[groupName] = { name: groupName, members: membersList, invited_members: [], password: password, owner: player } this.updateGroupsList() this.saveData() } updateInvitationNotification(){ const notificationButton = $("#groupChatGroupNotification") if (Object.keys(this.invitations).length){ notificationButton.removeClass("groupChatGroupNotificationInactive") notificationButton.addClass("groupChatGroupNotificationActive") } else { notificationButton.removeClass("groupChatGroupNotificationActive") notificationButton.addClass("groupChatGroupNotificationInactive") } } } const plugin = new GroupChatPlugin(); IdlePixelPlus.registerPlugin(plugin); })();