- // ==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://gf.qytechs.cn/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905
- // @require https://gf.qytechs.cn/scripts/484046/code/IdlePixel%2B%20Custom%20Handling.js?anticache=20240721
- // @require https://gf.qytechs.cn/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);
-
- })();