mirror of
https://github.com/Equicord/Equicord.git
synced 2025-01-18 13:23:28 -05:00
Add 2 Sqaaakoi Plugins (hehe)
This commit is contained in:
parent
f5eb72d099
commit
1f3eb2e61a
9 changed files with 522 additions and 2 deletions
|
@ -130,7 +130,9 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
|
|||
- VencordRPC by AutumnVN
|
||||
- VideoSpeed by Samwich
|
||||
- ViewRaw2 by Kyuuhachi
|
||||
- VoiceChannelLog by Sqaaakoi (maintained by thororen)
|
||||
- VoiceChatUtilities by D3SOX
|
||||
- VoiceJoinMessages by Sqaaakoi (maintained by thororen)
|
||||
- WebpackTarball by Kyuuhachi
|
||||
- WhosWatching by fres
|
||||
- WigglyText by nexpid
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Flex } from "@components/Flex";
|
|||
import { closeModal, ModalContent, ModalRoot, openModal } from "@utils/modal";
|
||||
import { Clickable, Forms } from "@webpack/common";
|
||||
|
||||
import { cl, getEmojiUrl,SoundLogEntry, User } from "../utils";
|
||||
import { cl, getEmojiUrl, SoundLogEntry, User } from "../utils";
|
||||
|
||||
export function openMoreUsersModal(item: SoundLogEntry, users: User[], onClickUser: Function) {
|
||||
const key = openModal(props => (
|
||||
|
@ -21,7 +21,6 @@ export function openMoreUsersModal(item: SoundLogEntry, users: User[], onClickUs
|
|||
));
|
||||
}
|
||||
|
||||
|
||||
export default function MoreUsersModal({ item, users, onClickUser, closeModal }: { item: SoundLogEntry, users: User[], onClickUser: Function, closeModal: Function; }) {
|
||||
return (
|
||||
<ModalContent className={cl("more")}>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
.vc-voice-channel-log {
|
||||
list-style-type: none;
|
||||
height: 50px;
|
||||
display: grid;
|
||||
grid-template-columns: 2.25rem 24px 40px max-content;
|
||||
gap: 4px;
|
||||
width: 4px;
|
||||
background-color: var(--background-modifier-active);
|
||||
}
|
||||
|
||||
.vc-voice-channel-log-date-separator,
|
||||
.vc-voice-channel-log-timestamp {
|
||||
margin-left: 4px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
font-size: .75rem;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
color: var(--text-normal, white);
|
||||
}
|
||||
|
||||
.vc-voice-channel-log-avatar {
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px
|
||||
}
|
||||
|
||||
.vc-voice-channel-log-icon {
|
||||
margin: auto 3px;
|
||||
margin-left: 15px;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./VoiceChannelLogEntryComponent.css";
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { React, Timestamp, UserStore } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
import { Util } from "Vencord";
|
||||
|
||||
import { cl } from "..";
|
||||
import { VoiceChannelLogEntry } from "../logs";
|
||||
import Icon from "./VoiceChannelLogEntryIcons";
|
||||
|
||||
export function VoiceChannelLogEntryComponent({ logEntry, channel }: { logEntry: VoiceChannelLogEntry; channel: Channel; }) {
|
||||
const user = UserStore.getUser(logEntry.userId);
|
||||
return <li className="vc-voice-channel-log">
|
||||
<Timestamp className={cl("timestamp")} timestamp={new Date(logEntry.timestamp)} compact isInline={false} cozyAlt></Timestamp>
|
||||
<Icon logEntry={logEntry} channel={channel} className={cl("icon")} />
|
||||
<img
|
||||
className={classes(cl("avatar"))}
|
||||
onClick={() => Util.openUserProfile(logEntry.userId)}
|
||||
src={user.getAvatarURL(channel.getGuildId())}
|
||||
/>
|
||||
<div className={cl("content")}>
|
||||
{ }
|
||||
</div>
|
||||
</li>;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { React } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
|
||||
import { cl } from "..";
|
||||
import { VoiceChannelLogEntry } from "../logs";
|
||||
|
||||
export default function Icon({ logEntry, channel, className }: { logEntry: VoiceChannelLogEntry; channel: Channel; className: string; }) {
|
||||
// Taken from /assets/7378a83d74ce97d83380.svg
|
||||
const Join = <svg xmlns="http://www.w3.org/2000/svg" height="18" width="18"><g fill="none" fill-rule="evenodd"><path d="m18 0h-18v18h18z" /><path d="m0 8h14.2l-3.6-3.6 1.4-1.4 6 6-6 6-1.4-1.4 3.6-3.6h-14.2" fill="#3ba55c" /></g></svg>;
|
||||
// Taken from /assets/192510ade1abc3149b46.svg
|
||||
const Leave = <svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" ><g fill="none" fill-rule="evenodd"><path d="m18 0h-18v18h18z" /><path d="m3.8 8 3.6-3.6-1.4-1.4-6 6 6 6 1.4-1.4-3.6-3.6h14.2v-2" fill="#ed4245" /></g></svg>;
|
||||
// For other contributors, please DO make specific designs for these instead of how I just copied the join/leave icons and making them orange
|
||||
const MovedTo = <svg xmlns="http://www.w3.org/2000/svg" height="18" width="18"><g fill="none" fill-rule="evenodd"><path d="m18 0h-18v18h18z" /><path d="m3.8 8 3.6-3.6-1.4-1.4-6 6 6 6 1.4-1.4-3.6-3.6h14.2v-2" fill="#faa61a" /></g></svg>;
|
||||
const MovedFrom = <svg xmlns="http://www.w3.org/2000/svg" height="18" width="18"><g fill="none" fill-rule="evenodd"><path d="m18 0h-18v18h18z" /><path d="m0 8h14.2l-3.6-3.6 1.4-1.4 6 6-6 6-1.4-1.4 3.6-3.6h-14.2" fill="#faa61a" /></g></svg>;
|
||||
|
||||
if (logEntry.newChannel && !logEntry.oldChannel) return React.cloneElement(Join, { className: classes(className, cl("join")) });
|
||||
if (!logEntry.newChannel && logEntry.oldChannel) return React.cloneElement(Leave, { className: classes(className, cl("leave")) });
|
||||
if (logEntry.newChannel === channel.id && logEntry.oldChannel) return React.cloneElement(MovedFrom, { className: classes(className, cl("moved-from")) });
|
||||
if (logEntry.newChannel && logEntry.oldChannel === channel.id) return React.cloneElement(MovedTo, { className: classes(className, cl("moved-to")) });
|
||||
// we should never get here, this is just here to shut up the type checker
|
||||
return <svg></svg>;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { findStoreLazy } from "@webpack";
|
||||
import { React, ScrollerThin, Text } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
|
||||
import { cl } from "..";
|
||||
import { getVcLogs, vcLogSubscribe } from "../logs";
|
||||
import { VoiceChannelLogEntryComponent } from "./VoiceChannelLogEntryComponent";
|
||||
|
||||
const AccessibilityStore = findStoreLazy("AccessibilityStore");
|
||||
|
||||
export function openVoiceChannelLog(channel: Channel) {
|
||||
return openModal(props => (
|
||||
<VoiceChannelLogModal
|
||||
props={props}
|
||||
channel={channel}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
export function VoiceChannelLogModal({ channel, props }: { channel: Channel; props: ModalProps; }) {
|
||||
React.useSyncExternalStore(vcLogSubscribe, () => getVcLogs(channel.id));
|
||||
const vcLogs = getVcLogs(channel.id);
|
||||
const logElements: (React.ReactNode)[] = [];
|
||||
|
||||
if (vcLogs.length > 0) {
|
||||
for (let i = 0; i < vcLogs.length; i++) {
|
||||
const logEntry = vcLogs[i];
|
||||
if (i === 0 || logEntry.timestamp.toDateString() !== vcLogs[i - 1].timestamp.toDateString()) {
|
||||
logElements.push(<div className={classes(cl("date-separator"))} role="separator" aria-label={logEntry.timestamp.toDateString()}>
|
||||
<span>
|
||||
{logEntry.timestamp.toDateString()}
|
||||
</span>
|
||||
</div>);
|
||||
} else {
|
||||
logElements.push(<VoiceChannelLogEntryComponent logEntry={logEntry} channel={channel} />);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logElements.push(<div className={cl("empty")}>No logs to display.</div>);
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalRoot
|
||||
{...props}
|
||||
size={ModalSize.LARGE}
|
||||
>
|
||||
<ModalHeader>
|
||||
<Text className={cl("header")} variant="heading-lg/semibold" style={{ flexGrow: 1 }}>{channel.name} logs</Text>
|
||||
<ModalCloseButton onClick={props.onClose} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent>
|
||||
<ScrollerThin fade className={classes(cl("scroller"), `group-spacing-${AccessibilityStore.messageGroupSpacing}`)}>
|
||||
{logElements}
|
||||
</ScrollerThin>
|
||||
</ModalContent>
|
||||
</ModalRoot >
|
||||
);
|
||||
}
|
157
src/equicordplugins/voiceChannelLog/index.tsx
Normal file
157
src/equicordplugins/voiceChannelLog/index.tsx
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { FluxDispatcher, Menu, MessageActions, MessageStore, RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
import { Message, User } from "discord-types/general";
|
||||
|
||||
import { openVoiceChannelLog } from "./components/VoiceChannelLogModal";
|
||||
import { addLogEntry } from "./logs";
|
||||
|
||||
export const cl = classNameFactory("vc-voice-channel-log-");
|
||||
const createBotMessage = findByCodeLazy('username:"Clyde"');
|
||||
|
||||
const settings = definePluginSettings({
|
||||
mode: {
|
||||
type: OptionType.SELECT,
|
||||
description: "How to show the voice channel log",
|
||||
options: [
|
||||
{ label: "Log menu", value: 1, default: true },
|
||||
{ label: "Log to associated chat directly", value: 2 },
|
||||
{ label: "Log to chat and log menu", value: 3 },
|
||||
]
|
||||
},
|
||||
voiceChannelChatSelf: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Log your own voice channel events in the voice channels",
|
||||
default: true
|
||||
},
|
||||
voiceChannelChatSilent: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Join/leave/move messages in voice channel chats will be silent",
|
||||
default: true
|
||||
},
|
||||
voiceChannelChatSilentSelf: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Join/leave/move messages in voice channel chats will be silent if you are in the voice channel",
|
||||
default: false
|
||||
},
|
||||
ignoreBlockedUsers: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Do not log blocked users",
|
||||
default: false
|
||||
},
|
||||
});
|
||||
|
||||
interface VoiceState {
|
||||
guildId?: string;
|
||||
channelId?: string;
|
||||
oldChannelId?: string;
|
||||
user: User;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
function getMessageFlags(selfInChannel: boolean) {
|
||||
let flags = 1 << 6;
|
||||
if (selfInChannel ? settings.store.voiceChannelChatSilentSelf : settings.store.voiceChannelChatSilent) flags += 1 << 12;
|
||||
return flags;
|
||||
}
|
||||
|
||||
function sendVoiceStatusMessage(channelId: string, content: string, userId: string, selfInChannel: boolean): Message | null {
|
||||
if (!channelId) return null;
|
||||
const message: Message = createBotMessage({ channelId, content, embeds: [] });
|
||||
message.flags = getMessageFlags(selfInChannel);
|
||||
message.author = UserStore.getUser(userId);
|
||||
// If we try to send a message into an unloaded channel, the client-sided messages get overwritten when the channel gets loaded
|
||||
// This might be messy but It Works:tm:
|
||||
const messagesLoaded: Promise<any> = MessageStore.hasPresent(channelId) ? new Promise<void>(r => r()) : MessageActions.fetchMessages({ channelId });
|
||||
messagesLoaded.then(() => {
|
||||
FluxDispatcher.dispatch({
|
||||
type: "MESSAGE_CREATE",
|
||||
channelId,
|
||||
message,
|
||||
optimistic: true,
|
||||
sendMessageOptions: {},
|
||||
isPushNotification: false
|
||||
});
|
||||
});
|
||||
return message;
|
||||
}
|
||||
|
||||
const patchChannelContextMenu: NavContextMenuPatchCallback = (children, { channel }) => {
|
||||
const group = findGroupChildrenByChildId("mark-channel-read", children) ?? children;
|
||||
group.push(
|
||||
<Menu.MenuItem
|
||||
id="vc-view-voice-channel-logs"
|
||||
label="View Channel Logs"
|
||||
action={() => { openVoiceChannelLog(channel); }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// Blatantly stolen from VcNarrator plugin
|
||||
|
||||
// For every user, channelId and oldChannelId will differ when moving channel.
|
||||
// Only for the local user, channelId and oldChannelId will be the same when moving channel,
|
||||
// for some ungodly reason
|
||||
let clientOldChannelId: string | undefined;
|
||||
|
||||
export default definePlugin({
|
||||
name: "VoiceChannelLog",
|
||||
description: "Logs who joins and leaves voice channels",
|
||||
authors: [Devs.Sqaaakoi, EquicordDevs.thororen],
|
||||
contextMenus: {
|
||||
"channel-context": patchChannelContextMenu
|
||||
},
|
||||
settings,
|
||||
flux: {
|
||||
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
|
||||
const clientUserId = UserStore.getCurrentUser().id;
|
||||
voiceStates.forEach(state => {
|
||||
// mmmm hacky workaround
|
||||
const { userId, channelId } = state;
|
||||
let { oldChannelId } = state;
|
||||
if (userId === clientUserId && channelId !== clientOldChannelId) {
|
||||
oldChannelId = clientOldChannelId;
|
||||
clientOldChannelId = channelId;
|
||||
}
|
||||
if (settings.store.ignoreBlockedUsers && RelationshipStore.isBlocked(userId)) return;
|
||||
// Ignore events from same channel
|
||||
if (oldChannelId === channelId) return;
|
||||
|
||||
const logEntry = {
|
||||
userId,
|
||||
oldChannel: oldChannelId || null,
|
||||
newChannel: channelId || null,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
addLogEntry(logEntry, oldChannelId);
|
||||
addLogEntry(logEntry, channelId);
|
||||
|
||||
if (!settings.store.voiceChannelChatSelf && userId === clientUserId) return;
|
||||
// Join / Leave
|
||||
if ((!oldChannelId && channelId) || (oldChannelId && !channelId)) {
|
||||
// empty string is to make type checker shut up
|
||||
const targetChannelId = oldChannelId || channelId || "";
|
||||
const selfInChannel = SelectedChannelStore.getVoiceChannelId() === targetChannelId;
|
||||
sendVoiceStatusMessage(targetChannelId, `${(channelId ? "Joined" : "Left")} <#${targetChannelId}>`, userId, selfInChannel);
|
||||
}
|
||||
// Move between channels
|
||||
if (oldChannelId && channelId) {
|
||||
sendVoiceStatusMessage(oldChannelId, `Moved to <#${channelId}>`, userId, SelectedChannelStore.getVoiceChannelId() === oldChannelId);
|
||||
sendVoiceStatusMessage(channelId, `Moved from <#${oldChannelId}>`, userId, SelectedChannelStore.getVoiceChannelId() === channelId);
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
40
src/equicordplugins/voiceChannelLog/logs.ts
Normal file
40
src/equicordplugins/voiceChannelLog/logs.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export interface VoiceChannelLogEntry {
|
||||
userId: string;
|
||||
oldChannel: string | null;
|
||||
newChannel: string | null;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export const vcLogs = new Map<string, VoiceChannelLogEntry[]>();
|
||||
let vcLogSubscriptions: (() => void)[] = [];
|
||||
|
||||
export function getVcLogs(channel?: string): VoiceChannelLogEntry[] {
|
||||
if (!channel) return [];
|
||||
if (!vcLogs.has(channel)) vcLogs.set(channel, []);
|
||||
return vcLogs.get(channel) || [];
|
||||
}
|
||||
|
||||
export function addLogEntry(logEntry: VoiceChannelLogEntry, channel?: string) {
|
||||
if (!channel) return;
|
||||
vcLogs.set(channel, [...getVcLogs(channel), logEntry]);
|
||||
vcLogSubscriptions.forEach(u => u());
|
||||
}
|
||||
|
||||
export function clearLogs(channel?: string) {
|
||||
if (!channel) return;
|
||||
vcLogs.set(channel, []);
|
||||
vcLogSubscriptions.forEach(u => u());
|
||||
}
|
||||
|
||||
export function vcLogSubscribe(listener: () => void) {
|
||||
vcLogSubscriptions = [...vcLogSubscriptions, listener];
|
||||
return () => {
|
||||
vcLogSubscriptions = vcLogSubscriptions.filter(l => l !== listener);
|
||||
};
|
||||
}
|
153
src/equicordplugins/voiceJoinMessages/index.ts
Normal file
153
src/equicordplugins/voiceJoinMessages/index.ts
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { humanFriendlyJoin } from "@utils/text";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||
import { ChannelStore, FluxDispatcher, MessageActions, MessageStore, RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
import { Message, User } from "discord-types/general";
|
||||
|
||||
const createBotMessage = findByCodeLazy('username:"Clyde"');
|
||||
const SortedVoiceStateStore = findByPropsLazy("getVoiceStatesForChannel", "getCurrentClientVoiceChannelId");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
friendDirectMessages: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Recieve notifications in your friends' DMs when they join a voice channel",
|
||||
default: true
|
||||
},
|
||||
friendDirectMessagesShowMembers: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show a list of other members in the voice channel when recieving a DM notification of your friend joining a voice channel",
|
||||
default: true
|
||||
},
|
||||
friendDirectMessagesShowMemberCount: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show the count of other members in the voice channel when recieving a DM notification of your friend joining a voice channel",
|
||||
default: false
|
||||
},
|
||||
friendDirectMessagesSelf: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Recieve notifications in your friends' DMs even if you are in the same voice channel as them",
|
||||
default: false
|
||||
},
|
||||
friendDirectMessagesSilent: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Join messages in your friends DMs will be silent",
|
||||
default: false
|
||||
},
|
||||
allowedFriends: {
|
||||
type: OptionType.STRING,
|
||||
description: "Comma or space separated list of friends' user IDs you want to recieve join messages from",
|
||||
default: ""
|
||||
},
|
||||
ignoreBlockedUsers: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Do not send messages about blocked users joining/leaving/moving voice channels",
|
||||
default: true
|
||||
},
|
||||
});
|
||||
|
||||
interface VoiceState {
|
||||
guildId?: string;
|
||||
channelId?: string;
|
||||
oldChannelId?: string;
|
||||
user: User;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
function getMessageFlags() {
|
||||
let flags = 1 << 6;
|
||||
if (settings.store.friendDirectMessagesSilent) flags += 1 << 12;
|
||||
return flags;
|
||||
}
|
||||
|
||||
function sendVoiceStatusMessage(channelId: string, content: string, userId: string): Message | null {
|
||||
if (!channelId) return null;
|
||||
const message: Message = createBotMessage({ channelId, content, embeds: [] });
|
||||
message.flags = getMessageFlags();
|
||||
message.author = UserStore.getUser(userId);
|
||||
// If we try to send a message into an unloaded channel, the client-sided messages get overwritten when the channel gets loaded
|
||||
// This might be messy but It Works:tm:
|
||||
const messagesLoaded: Promise<any> = MessageStore.hasPresent(channelId) ? new Promise<void>(r => r()) : MessageActions.fetchMessages({ channelId });
|
||||
messagesLoaded.then(() => {
|
||||
FluxDispatcher.dispatch({
|
||||
type: "MESSAGE_CREATE",
|
||||
channelId,
|
||||
message,
|
||||
optimistic: true,
|
||||
sendMessageOptions: {},
|
||||
isPushNotification: false
|
||||
});
|
||||
});
|
||||
return message;
|
||||
}
|
||||
|
||||
function isFriendAllowlisted(friendId: string) {
|
||||
if (!RelationshipStore.isFriend(friendId)) return false;
|
||||
const list = settings.store.allowedFriends.split(",").join(" ").split(" ").filter(i => i.length > 0);
|
||||
if (list.join(" ").length < 1) return true;
|
||||
return list.includes(friendId);
|
||||
}
|
||||
|
||||
// Blatantly stolen from VcNarrator plugin
|
||||
|
||||
// For every user, channelId and oldChannelId will differ when moving channel.
|
||||
// Only for the local user, channelId and oldChannelId will be the same when moving channel,
|
||||
// for some ungodly reason
|
||||
let clientOldChannelId: string | undefined;
|
||||
|
||||
export default definePlugin({
|
||||
name: "VoiceJoinMessages",
|
||||
description: "Recieve client-side ephemeral messages when your friends join voice channels",
|
||||
authors: [Devs.Sqaaakoi, EquicordDevs.thororen],
|
||||
settings,
|
||||
flux: {
|
||||
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
|
||||
const clientUserId = UserStore.getCurrentUser().id;
|
||||
for (const state of voiceStates) {
|
||||
// mmmm hacky workaround
|
||||
const { userId, channelId } = state;
|
||||
let { oldChannelId } = state;
|
||||
if (userId === clientUserId && channelId !== clientOldChannelId) {
|
||||
oldChannelId = clientOldChannelId;
|
||||
clientOldChannelId = channelId;
|
||||
}
|
||||
if (settings.store.ignoreBlockedUsers && RelationshipStore.isBlocked(userId)) return;
|
||||
// Ignore events from same channel
|
||||
if (oldChannelId === channelId) return;
|
||||
|
||||
// Friend joined a voice channel
|
||||
if (settings.store.friendDirectMessages && (!oldChannelId && channelId) && userId !== clientUserId && isFriendAllowlisted(userId)) {
|
||||
const selfInChannel = SelectedChannelStore.getVoiceChannelId() === channelId;
|
||||
let memberListContent = "";
|
||||
if (settings.store.friendDirectMessagesShowMembers || settings.store.friendDirectMessagesShowMemberCount) {
|
||||
const voiceState = SortedVoiceStateStore.getVoiceStatesForChannel(channelId);
|
||||
const sortedVoiceStates: User[] = Object.values(voiceState as { [key: string]: VoiceState; })
|
||||
.filter((voiceState: VoiceState) => { voiceState.user && voiceState.user.id !== userId; })
|
||||
.map((voiceState: VoiceState) => voiceState.user);
|
||||
console.log(sortedVoiceStates);
|
||||
const otherMembers = sortedVoiceStates.filter(s => s.id !== userId);
|
||||
const otherMembersCount = otherMembers.length;
|
||||
if (otherMembersCount <= 0) {
|
||||
memberListContent += ", nobody else is in the voice channel";
|
||||
} else if (settings.store.friendDirectMessagesShowMemberCount) {
|
||||
memberListContent += ` with ${otherMembersCount} other member${otherMembersCount === 1 ? "s" : ""}`;
|
||||
}
|
||||
if (settings.store.friendDirectMessagesShowMembers && otherMembersCount > 0) {
|
||||
memberListContent += settings.store.friendDirectMessagesShowMemberCount ? ", " : " with ";
|
||||
memberListContent += humanFriendlyJoin(otherMembers.map(s => `<@${s.id}>`));
|
||||
}
|
||||
}
|
||||
const dmChannelId = ChannelStore.getDMFromUserId(userId);
|
||||
if (dmChannelId && (selfInChannel ? settings.store.friendDirectMessagesSelf : true)) sendVoiceStatusMessage(dmChannelId, `Joined voice channel <#${channelId}>${memberListContent}`, userId);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
Loading…
Reference in a new issue