sx.blah.discord.handle.impl.obj.Channel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Discord4J Show documentation
Show all versions of Discord4J Show documentation
A Java binding for the official Discord API, forked from the inactive https://github.com/nerd/Discord4J. Copyright (c) 2017, Licensed under GNU LGPLv3
The newest version!
/*
* This file is part of Discord4J.
*
* Discord4J is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Discord4J is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Discord4J. If not, see .
*/
package sx.blah.discord.handle.impl.obj;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.http.HttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.message.BasicNameValuePair;
import sx.blah.discord.Discord4J;
import sx.blah.discord.api.IDiscordClient;
import sx.blah.discord.api.IShard;
import sx.blah.discord.api.internal.DiscordClientImpl;
import sx.blah.discord.api.internal.DiscordEndpoints;
import sx.blah.discord.api.internal.DiscordUtils;
import sx.blah.discord.api.internal.json.objects.*;
import sx.blah.discord.api.internal.json.requests.*;
import sx.blah.discord.handle.impl.events.guild.channel.webhook.WebhookCreateEvent;
import sx.blah.discord.handle.impl.events.guild.channel.webhook.WebhookDeleteEvent;
import sx.blah.discord.handle.impl.events.guild.channel.webhook.WebhookUpdateEvent;
import sx.blah.discord.handle.obj.*;
import sx.blah.discord.util.*;
import sx.blah.discord.util.cache.Cache;
import sx.blah.discord.util.cache.LongMap;
import java.io.*;
import java.time.Instant;
import java.time.Period;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* The default implementation of {@link IChannel}.
*/
public class Channel implements IChannel {
/**
* The number of messages to fetch from Discord per message history request.
*/
public static final int MESSAGE_CHUNK_COUNT = 100; //100 is the max amount discord lets you retrieve at one time
/**
* The name of the channel.
*/
protected volatile String name;
/**
* The unique snowflake ID of the channel.
*/
protected final long id;
/**
* The cached messages that have been sent in the channel.
*/
public final Cache messages;
/**
* The parent guild of the channel.
*/
protected final IGuild guild;
/**
* The channel's topic message.
*/
protected volatile String topic;
/**
* Holds a reference to the task responsible for maintaining typing status.
*/
private AtomicReference typingTask = new AtomicReference<>(null);
/**
* Manages all TimerTasks which send typing statuses.
*/
protected static final Timer typingTimer = new Timer("Typing Status Timer", true);
/**
* The period of time, in seconds, before another typing update must be sent to maintain typing status.
*/
protected static final long TIME_FOR_TYPE_STATUS = 10000;
/**
* The position of the channel in the channel list.
*/
protected volatile int position;
/**
* The permission overrides for users.
*/
public final Cache userOverrides;
/**
* The permission overrides for roles.
*/
public final Cache roleOverrides;
/**
* The webhooks for the channel.
*/
protected final Cache webhooks;
/**
* Whether the channel is nsfw.
*/
protected boolean isNSFW;
protected volatile long categoryID;
/**
* The client that owns the channel object.
*/
protected final DiscordClientImpl client;
public Channel(DiscordClientImpl client, String name, long id, IGuild guild, String topic, int position, boolean isNSFW, long categoryID,
Cache roleOverrides,
Cache userOverrides) {
this.client = client;
this.name = name;
this.id = id;
this.guild = guild;
this.topic = topic;
this.position = position;
this.roleOverrides = roleOverrides;
this.userOverrides = userOverrides;
this.isNSFW = isNSFW;
this.messages = new Cache<>(client, IMessage.class);
this.webhooks = new Cache<>(client, IWebhook.class);
this.categoryID = categoryID;
}
@Override
public String getName() {
return name;
}
/**
* Sets the CACHED name of the channel.
*
* @param name The name of the channel.
*/
public void setName(String name) {
this.name = name;
}
@Override
public long getLongID() {
return id;
}
/**
* Adds a message to the internal message CACHE.
*
* @param message The message to add.
*/
public void addToCache(IMessage message) {
if (getMaxInternalCacheCount() < 0) {
messages.put(message);
} else if (getMaxInternalCacheCount() != 0) {
if (getInternalCacheCount() == getMaxInternalCacheCount()) {
messages.remove(messages.longIDs().stream().mapToLong(it -> it).min().getAsLong()); //Lowest id should be the earliest
}
messages.put(message);
}
}
/**
* Makes a request to Discord for message history.
*
* @param before The ID of the message to get message history before.
* @param limit The maximum number of messages to request.
* @return The received messages.
*/
private IMessage[] getHistory(long before, int limit) {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.READ_MESSAGES);
String query = "?before=" + Long.toUnsignedString(before) + "&limit=" + limit;
MessageObject[] messages = client.REQUESTS.GET.makeRequest(
DiscordEndpoints.CHANNELS + getStringID() + "/messages" + query,
MessageObject[].class);
return Arrays.stream(messages).map(m -> DiscordUtils.getMessageFromJSON(this, m)).toArray(IMessage[]::new);
}
@Override
public MessageHistory getMessageHistory() {
return new MessageHistory(messages.values());
}
@Override
public MessageHistory getMessageHistory(int messageCount) {
if (messageCount <= messages.size()) { // we already have all of the wanted messages in the cache
return new MessageHistory(messages.values().stream()
.sorted(new MessageComparator(true))
.limit(messageCount)
.collect(Collectors.toList()));
} else {
List retrieved = new ArrayList<>(messageCount);
AtomicLong lastMessage = new AtomicLong(DiscordUtils.getSnowflakeFromTimestamp(Instant.now()));
int chunkSize = messageCount < MESSAGE_CHUNK_COUNT ? messageCount : MESSAGE_CHUNK_COUNT;
while (retrieved.size() < messageCount) { // while we dont have messageCount messages
IMessage[] chunk = getHistory(lastMessage.get(), chunkSize);
if (chunk.length == 0) break;
lastMessage.set(chunk[chunk.length - 1].getLongID());
Collections.addAll(retrieved, chunk);
}
return new MessageHistory(retrieved.size() > messageCount ? retrieved.subList(0, messageCount) : retrieved);
}
}
@Override
public MessageHistory getMessageHistoryFrom(Instant startDate) {
return getMessageHistoryFrom(startDate, Integer.MAX_VALUE);
}
@Override
public MessageHistory getMessageHistoryFrom(Instant startDate, int maxCount) {
return getMessageHistoryFrom(DiscordUtils.getSnowflakeFromTimestamp(startDate), maxCount);
}
@Override
public MessageHistory getMessageHistoryFrom(long id) {
return getMessageHistoryFrom(id, Integer.MAX_VALUE);
}
@Override
public MessageHistory getMessageHistoryFrom(long id, int maxCount) {
return getMessageHistoryIn(id, DiscordUtils.getSnowflakeFromTimestamp(getCreationDate()), maxCount);
}
@Override
public MessageHistory getMessageHistoryTo(Instant endDate) {
return getMessageHistoryTo(endDate, Integer.MAX_VALUE);
}
@Override
public MessageHistory getMessageHistoryTo(Instant endDate, int maxCount) {
return getMessageHistoryTo(DiscordUtils.getSnowflakeFromTimestamp(endDate), maxCount);
}
@Override
public MessageHistory getMessageHistoryTo(long id) {
return getMessageHistoryTo(id, Integer.MAX_VALUE);
}
@Override
public MessageHistory getMessageHistoryTo(long id, int maxCount) {
return getMessageHistoryIn(DiscordUtils.getSnowflakeFromTimestamp(Instant.now()), id, maxCount);
}
@Override
public MessageHistory getMessageHistoryIn(Instant startDate, Instant endDate) {
return getMessageHistoryIn(startDate, endDate, Integer.MAX_VALUE);
}
@Override
public MessageHistory getMessageHistoryIn(Instant startDate, Instant endDate, int maxCount) {
return getMessageHistoryIn(DiscordUtils.getSnowflakeFromTimestamp(startDate),
DiscordUtils.getSnowflakeFromTimestamp(endDate), maxCount);
}
@Override
public MessageHistory getMessageHistoryIn(long beginID, long endID) {
return getMessageHistoryIn(beginID, endID, Integer.MAX_VALUE);
}
@Override
public MessageHistory getMessageHistoryIn(long beginID, long endID, int maxCount) {
final List history = new ArrayList<>();
final int originalMaxCount = maxCount;
// Adds 1L so beginID will be included
long previousMessageID = beginID + 1L;
int added = -1;
while ((history.size() < originalMaxCount) && (added != 0)) {
maxCount = originalMaxCount - history.size();
final int chunkSize = (maxCount < MESSAGE_CHUNK_COUNT) ? maxCount : MESSAGE_CHUNK_COUNT;
final IMessage[] chunk = getHistory(previousMessageID, chunkSize);
added = 0;
for (final IMessage message : chunk) {
if (message.getLongID() >= endID) {
// We want to EXCLUDE previous messages later
previousMessageID = message.getLongID() - 1L;
history.add(message);
added++;
} else { // We don't need anything else
return new MessageHistory(history);
}
}
}
return new MessageHistory(history);
}
@Override
public MessageHistory getFullMessageHistory() {
return getMessageHistoryTo(getCreationDate());
}
@Override
public List bulkDelete() {
return bulkDelete(getMessageHistoryTo(Instant.now().minus(Period.ofWeeks(2))));
}
@Override
public List bulkDelete(List messages) {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_MESSAGES);
if (isPrivate())
throw new UnsupportedOperationException("Cannot bulk delete in private channels!");
if (messages.size() == 1) { //Skip further processing if only one message was provided
messages.get(0).delete();
return messages;
}
List toDelete = messages.stream()
.filter(msg -> msg.getLongID() >= (((System.currentTimeMillis() - 14 * 24 * 60 * 60 * 1000) - 1420070400000L) << 22)) // Taken from Jake
.distinct()
.collect(Collectors.toList());
if (toDelete.size() < 1)
throw new DiscordException("Must provide at least 1 valid message to delete.");
if (toDelete.size() == 1) { //Bulk delete is no longer valid, time for normal delete.
toDelete.get(0).delete();
return toDelete;
} else if (toDelete.size() > 100) { //Above the max limit, time to create a sublist
Discord4J.LOGGER.warn(LogMarkers.HANDLE, "More than 100 messages requested to be bulk deleted! Bulk deleting only the first 100...");
toDelete = toDelete.subList(0, 100);
}
client.REQUESTS.POST.makeRequest(
DiscordEndpoints.CHANNELS + id + "/messages/bulk-delete",
new BulkDeleteRequest(toDelete));
return toDelete;
}
@Override
public int getMaxInternalCacheCount() {
return client.getMaxCacheCount();
}
@Override
public int getInternalCacheCount() {
synchronized (messages) {
return messages.size();
}
}
@Override
public IMessage getMessageByID(long messageID) {
return messages.get(messageID);
}
@Override
public IMessage fetchMessage(long messageID) {
return messages.getOrElseGet(messageID, () -> {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.READ_MESSAGES, Permissions.READ_MESSAGE_HISTORY);
return RequestBuffer.request(() ->
(IMessage) DiscordUtils.getMessageFromJSON(this, client.REQUESTS.GET.makeRequest(
DiscordEndpoints.CHANNELS + this.getStringID() + "/messages/" + Long.toUnsignedString(messageID),
MessageObject.class))
).get();
});
}
@Override
public IGuild getGuild() {
return guild;
}
@Override
public boolean isPrivate() {
return this instanceof PrivateChannel;
}
@Override
public boolean isNSFW() {
return isNSFW || DiscordUtils.NSFW_CHANNEL_PATTERN.matcher(name).find();
}
/**
* Sets the CACHED nsfw state for the channel.
*
* @param isNSFW The new channel nsfw state.
*/
public void setNSFW(boolean isNSFW) {
this.isNSFW = isNSFW;
}
@Override
public String getTopic() {
return topic;
}
/**
* Sets the CACHED topic for the channel.
*
* @param topic The channel topic.
*/
public void setTopic(String topic) {
this.topic = topic;
}
@Override
public String mention() {
return "<#" + this.getStringID() + ">";
}
@Override
public IMessage sendMessage(String content) {
return sendMessage(content, false);
}
@Override
public IMessage sendMessage(EmbedObject embed) {
return sendMessage(null, embed);
}
@Override
public IMessage sendMessage(String content, boolean tts) {
return sendMessage(content, null, tts);
}
@Override
public IMessage sendMessage(String content, EmbedObject embed) {
return sendMessage(content, embed, false);
}
@Override
public IMessage sendMessage(String content, EmbedObject embed, boolean tts) {
getShard().checkReady("send message");
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.SEND_MESSAGES);
if (embed != null) {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.EMBED_LINKS);
}
MessageObject response = null;
try {
response = client.REQUESTS.POST.makeRequest(
DiscordEndpoints.CHANNELS+id+"/messages",
DiscordUtils.MAPPER_NO_NULLS.writeValueAsString(new MessageRequest(content, embed, tts)),
MessageObject.class);
} catch (JsonProcessingException e) {
Discord4J.LOGGER.error(LogMarkers.HANDLE, "Discord4J Internal Exception", e);
}
if (response == null || response.id == null) //Message didn't send
throw new DiscordException("Message was unable to be sent (Discord didn't return a response).");
return DiscordUtils.getMessageFromJSON(this, response);
}
@Override
public IMessage sendFile(File file) throws FileNotFoundException {
return sendFile((String) null, file);
}
@Override
public IMessage sendFiles(File... files) throws FileNotFoundException {
return sendFiles((String) null, files);
}
@Override
public IMessage sendFile(String content, File file) throws FileNotFoundException {
return sendFile(content, false, new FileInputStream(file), file.getName(), null);
}
@Override
public IMessage sendFiles(String content, File... files) throws FileNotFoundException {
return sendFiles(content, false, AttachmentPartEntry.from(files));
}
@Override
public IMessage sendFile(EmbedObject embed, File file) throws FileNotFoundException {
return sendFile(null, false, new FileInputStream(file), file.getName(), embed);
}
@Override
public IMessage sendFiles(EmbedObject embed, File... files) throws FileNotFoundException {
return sendFiles(null, false, embed, AttachmentPartEntry.from(files));
}
@Override
public IMessage sendFile(String content, InputStream file, String fileName) {
return sendFile(content, false, file, fileName, null);
}
@Override
public IMessage sendFiles(String content, AttachmentPartEntry... entry) {
return sendFiles(content, false, null, entry);
}
@Override
public IMessage sendFile(EmbedObject embed, InputStream file, String fileName) {
return sendFile(null, false, file, fileName, embed);
}
@Override
public IMessage sendFiles(EmbedObject embed, AttachmentPartEntry... entries) {
return sendFiles(null, false, embed, entries);
}
@Override
public IMessage sendFile(String content, boolean tts, InputStream file, String fileName) {
return sendFile(content, tts, file, fileName, null);
}
@Override
public IMessage sendFiles(String content, boolean tts, AttachmentPartEntry... entries) {
return sendFiles(content, tts, null, entries);
}
@Override
public IMessage sendFile(String content, boolean tts, InputStream file, String fileName, EmbedObject embed) {
return sendFiles(content, tts, embed, new AttachmentPartEntry(fileName, file));
}
@Override
public IMessage sendFiles(String content, boolean tts, EmbedObject embed, AttachmentPartEntry... entries) {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.SEND_MESSAGES, Permissions.ATTACH_FILES);
try {
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
if (entries.length == 1) {
builder.addBinaryBody("file", entries[0].getFileData(), ContentType.APPLICATION_OCTET_STREAM, entries[0].getFileName());
} else {
for (int i = 0; i < entries.length; i++) {
builder.addBinaryBody("file" + i, entries[i].getFileData(), ContentType.APPLICATION_OCTET_STREAM, entries[i].getFileName());
}
}
builder.addTextBody("payload_json", DiscordUtils.MAPPER_NO_NULLS.writeValueAsString(new FilePayloadObject(content, tts, embed)),
ContentType.MULTIPART_FORM_DATA.withCharset("UTF-8"));
HttpEntity fileEntity = builder.build();
MessageObject messageObject = DiscordUtils.MAPPER.readValue(client.REQUESTS.POST.makeRequest(DiscordEndpoints.CHANNELS + id + "/messages",
fileEntity, new BasicNameValuePair("Content-Type", "multipart/form-data")), MessageObject.class);
return DiscordUtils.getMessageFromJSON(this, messageObject);
} catch (IOException e) {
throw new DiscordException("JSON Parsing exception!", e);
}
}
@Override
public IMessage sendFile(MessageBuilder builder, InputStream file, String fileName) {
return sendFile(builder.getContent() != null && builder.getContent().isEmpty() ? null : builder.getContent(),
builder.isUsingTTS(), file, fileName, builder.getEmbedObject());
}
@Override
public IExtendedInvite createInvite(int maxAge, int maxUses, boolean temporary, boolean unique) {
getShard().checkReady("create invite");
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.CREATE_INVITE);
ExtendedInviteObject response = (client).REQUESTS.POST.makeRequest(
DiscordEndpoints.CHANNELS+getStringID()+"/invites",
new InviteCreateRequest(maxAge, maxUses, temporary, unique),
ExtendedInviteObject.class);
return DiscordUtils.getExtendedInviteFromJSON(client, response);
}
@Override
public synchronized void toggleTypingStatus() {
setTypingStatus(!getTypingStatus());
}
@Override
public void setTypingStatus(boolean typing) {
if (typing) {
TimerTask task = new TimerTask() {
@Override
public void run() {
if (!isPrivate() && isDeleted()) {
this.cancel();
return;
}
try {
Discord4J.LOGGER.trace(LogMarkers.HANDLE, "Sending TypingStatus Keep Alive");
((DiscordClientImpl) client).REQUESTS.POST.makeRequest(DiscordEndpoints.CHANNELS + getLongID() + "/typing");
} catch (RateLimitException | DiscordException e) {
Discord4J.LOGGER.error(LogMarkers.HANDLE, "Discord4J Internal Exception", e);
}
}
};
if (typingTask.compareAndSet(null, task)) {
typingTimer.scheduleAtFixedRate(task, 0, TIME_FOR_TYPE_STATUS);
}
} else {
TimerTask oldTask = typingTask.getAndSet(null);
if (oldTask != null) {
oldTask.cancel();
}
}
}
@Override
public synchronized boolean getTypingStatus() {
return typingTask.get() != null;
}
/**
* Sends a request to edit the channel.
*
* @param request The request object describing the changes to make.
*/
private void edit(ChannelEditRequest request) {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_CHANNEL, Permissions.MANAGE_CHANNELS);
try {
client.REQUESTS.PATCH.makeRequest(
DiscordEndpoints.CHANNELS + id,
DiscordUtils.MAPPER.writeValueAsString(request));
} catch (JsonProcessingException e) {
Discord4J.LOGGER.error(LogMarkers.HANDLE, "Discord4J Internal Exception", e);
}
}
@Override
public void edit(String name, int position, String topic) {
if (name == null || !DiscordUtils.CHANNEL_NAME_PATTERN.matcher(name).matches())
throw new IllegalArgumentException("Channel name must be 2-100 alphanumeric OR non-ASCII characters.");
edit(new ChannelEditRequest.Builder().name(name).position(position).topic(topic).build());
}
@Override
public void changeName(String name) {
if (name == null || !DiscordUtils.CHANNEL_NAME_PATTERN.matcher(name).matches())
throw new IllegalArgumentException("Channel name must be 2-100 alphanumeric OR non-ASCII characters.");
edit(new ChannelEditRequest.Builder().name(name).build());
}
@Override
public void changePosition(int position) {
edit(new ChannelEditRequest.Builder().position(position).build());
}
@Override
public void changeTopic(String topic) {
edit(new ChannelEditRequest.Builder().topic(topic).build());
}
@Override
public void changeNSFW(boolean isNSFW) {
edit(new ChannelEditRequest.Builder().nsfw(isNSFW).build());
}
@Override
public int getPosition() {
return getGuild().getChannels().indexOf(this);
}
/**
* Sets the CACHED position of the channel.
*
* @param position The position.
*/
public void setPosition(int position) {
this.position = position;
}
@Override
public void delete() {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_CHANNELS);
((DiscordClientImpl) client).REQUESTS.DELETE.makeRequest(DiscordEndpoints.CHANNELS+id);
}
@Override
public LongMap getUserOverrides() {
return userOverrides.mapCopy();
}
@Override
public LongMap getRoleOverrides() {
return roleOverrides.mapCopy();
}
@Override
public EnumSet getModifiedPermissions(IUser user) {
return PermissionUtils.getModifiedPermissions(user, guild, userOverrides, roleOverrides);
}
@Override
public EnumSet getModifiedPermissions(IRole role) {
return PermissionUtils.getModifiedPermissions(role, roleOverrides);
}
@Override
public void removePermissionsOverride(IUser user) {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_PERMISSIONS);
((DiscordClientImpl) client).REQUESTS.DELETE.makeRequest(DiscordEndpoints.CHANNELS+getStringID()+"/permissions/"+user.getStringID());
userOverrides.remove(user.getLongID());
}
@Override
public void removePermissionsOverride(IRole role) {
PermissionUtils.requireHierarchicalPermissions(this, client.getOurUser(), Collections.singletonList(role), Permissions.MANAGE_PERMISSIONS);
((DiscordClientImpl) client).REQUESTS.DELETE.makeRequest(DiscordEndpoints.CHANNELS+getStringID()+"/permissions/"+role.getStringID());
roleOverrides.remove(role.getLongID());
}
@Override
public void overrideRolePermissions(IRole role, EnumSet toAdd, EnumSet toRemove) {
overridePermissions("role", role.getStringID(), toAdd, toRemove);
}
@Override
public void overrideUserPermissions(IUser user, EnumSet toAdd, EnumSet toRemove) {
overridePermissions("member", user.getStringID(), toAdd, toRemove);
}
/**
* Makes a request to Discord to override permissions for a role or member.
*
* @param type The type of override to make. Either "role" or "member".
* @param id The ID of the role or member to make the override for.
* @param toAdd The permissions to explicitly allow.
* @param toRemove The permissions to explicitly deny.
*/
private void overridePermissions(String type, String id, EnumSet toAdd, EnumSet toRemove) {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_PERMISSIONS);
((DiscordClientImpl) client).REQUESTS.PUT.makeRequest(
DiscordEndpoints.CHANNELS+getStringID()+"/permissions/"+id,
new OverwriteObject(type, null, Permissions.generatePermissionsNumber(toAdd), Permissions.generatePermissionsNumber(toRemove)));
}
@Override
public List getExtendedInvites() {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_CHANNEL);
ExtendedInviteObject[] response = client.REQUESTS.GET.makeRequest(
DiscordEndpoints.CHANNELS + id + "/invites",
ExtendedInviteObject[].class);
List invites = new ArrayList<>();
for (ExtendedInviteObject inviteResponse : response)
invites.add(DiscordUtils.getExtendedInviteFromJSON(client, inviteResponse));
return invites;
}
@Override
public List getUsersHere() {
return guild.getUsers().stream().filter((user) -> {
EnumSet permissions = getModifiedPermissions(user);
return Permissions.READ_MESSAGES.hasPermission(Permissions.generatePermissionsNumber(permissions), true);
}).collect(Collectors.toList());
}
@Override
public List getPinnedMessages() {
List messages = new ArrayList<>();
MessageObject[] pinnedMessages = ((DiscordClientImpl) client).REQUESTS.GET.makeRequest(
DiscordEndpoints.CHANNELS + id + "/pins",
MessageObject[].class);
for (MessageObject message : pinnedMessages)
messages.add(DiscordUtils.getMessageFromJSON(this, message));
return messages;
}
@Override
public void pin(IMessage message) {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_MESSAGES);
if (!message.getChannel().equals(this))
throw new DiscordException("Message channel doesn't match current channel!");
if (message.isPinned())
throw new DiscordException("Message already pinned!");
((DiscordClientImpl) client).REQUESTS.PUT.makeRequest(DiscordEndpoints.CHANNELS + id + "/pins/" + message.getStringID());
}
@Override
public void unpin(IMessage message) {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_MESSAGES);
if (!message.getChannel().equals(this))
throw new DiscordException("Message channel doesn't match current channel!");
if (!message.isPinned())
throw new DiscordException("Message is not pinned!");
((DiscordClientImpl) client).REQUESTS.DELETE.makeRequest(DiscordEndpoints.CHANNELS + id + "/pins/" + message.getStringID());
}
@Override
public List getWebhooks() {
return new LinkedList<>(webhooks.values());
}
@Override
public IWebhook getWebhookByID(long id) {
return webhooks.get(id);
}
@Override
public List getWebhooksByName(String name) {
return webhooks.stream()
.filter(w -> w.getDefaultName().equals(name))
.collect(Collectors.toList());
}
@Override
public IWebhook createWebhook(String name) {
return createWebhook(name, Image.defaultAvatar());
}
@Override
public IWebhook createWebhook(String name, Image avatar) {
return createWebhook(name, avatar.getData());
}
@Override
public IWebhook createWebhook(String name, String avatar) {
getShard().checkReady("create webhook");
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_WEBHOOKS);
if (name == null || name.length() < 2 || name.length() > 32)
throw new DiscordException("Webhook name can only be between 2 and 32 characters!");
WebhookObject response = ((DiscordClientImpl) client).REQUESTS.POST.makeRequest(
DiscordEndpoints.CHANNELS + getStringID() + "/webhooks",
new WebhookCreateRequest(name, avatar),
WebhookObject.class);
IWebhook webhook = DiscordUtils.getWebhookFromJSON(this, response);
webhooks.put(webhook);
return webhook;
}
/**
* Forcibly loads and caches all webhooks for the channel.
*/
public void loadWebhooks() {
try {
PermissionUtils.requirePermissions(this, client.getOurUser(), Permissions.MANAGE_WEBHOOKS);
} catch (MissingPermissionsException ignored) {
return;
}
RequestBuffer.request(() -> {
try {
List oldList = getWebhooks()
.stream()
.map(IWebhook::copy)
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
WebhookObject[] response = ((DiscordClientImpl) client).REQUESTS.GET.makeRequest(
DiscordEndpoints.CHANNELS + getStringID() + "/webhooks",
WebhookObject[].class);
if (response != null) {
for (WebhookObject webhookObject : response) {
long webhookId = Long.parseUnsignedLong(webhookObject.id);
if (getWebhookByID(webhookId) == null) {
IWebhook newWebhook = DiscordUtils.getWebhookFromJSON(this, webhookObject);
client.getDispatcher().dispatch(new WebhookCreateEvent(newWebhook));
webhooks.put(newWebhook);
} else {
IWebhook toUpdate = getWebhookByID(webhookId);
IWebhook oldWebhook = toUpdate.copy();
toUpdate = DiscordUtils.getWebhookFromJSON(this, webhookObject);
if (!oldWebhook.getDefaultName().equals(toUpdate.getDefaultName()) || !String.valueOf(oldWebhook.getDefaultAvatar()).equals(String.valueOf(toUpdate.getDefaultAvatar())))
client.getDispatcher().dispatch(new WebhookUpdateEvent(oldWebhook, toUpdate));
oldList.remove(oldWebhook);
}
}
}
oldList.forEach(webhook -> {
webhooks.remove(webhook);
client.getDispatcher().dispatch(new WebhookDeleteEvent(webhook));
});
} catch (Exception e) {
Discord4J.LOGGER.warn(LogMarkers.HANDLE, "Discord4J Internal Exception", e);
}
});
}
@Override
public boolean isDeleted() {
return getGuild().getChannelByID(id) != this;
}
@Override
public void changeCategory(ICategory category) {
PermissionUtils.requirePermissions(this, getClient().getOurUser(), Permissions.MANAGE_CHANNELS);
Long id = category == null ? null : category.getLongID();
edit(new ChannelEditRequest.Builder().parentID(id).build());
}
@Override
public ICategory getCategory() {
if (categoryID == 0L) {
return null;
}
return getGuild().getCategoryByID(categoryID);
}
public void setCategoryID(long categoryId) {
this.categoryID = categoryId;
}
@Override
public IChannel copy() {
Channel channel = new Channel(client, name, id, guild, topic, position, isNSFW, categoryID,
new Cache<>(client, sx.blah.discord.handle.obj.PermissionOverride.class),
new Cache<>(client, sx.blah.discord.handle.obj.PermissionOverride.class));
channel.typingTask.set(typingTask.get());
channel.roleOverrides.putAll(roleOverrides);
channel.userOverrides.putAll(userOverrides);
return channel;
}
@Override
public IDiscordClient getClient() {
return client;
}
@Override
public IShard getShard() {
return getGuild().getShard();
}
@Override
public String toString() {
return mention();
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object other) {
return DiscordUtils.equals(this, other);
}
}