Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for threads #1613

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/net/dv8tion/jda/api/JDAInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
*/
public class JDAInfo
{
public static final int DISCORD_GATEWAY_VERSION = 8;
public static final int DISCORD_REST_VERSION = 8;
public static final int DISCORD_GATEWAY_VERSION = 9;
public static final int DISCORD_REST_VERSION = 9;
public static final int AUDIO_GATEWAY_VERSION = 4;
public static final String GITHUB = "https://github.com/DV8FromTheWorld/JDA";
public static final String VERSION_MAJOR = "@versionMajor@";
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/ChannelType.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ public enum ChannelType
* A {@link net.dv8tion.jda.api.entities.StoreChannel StoreChannel}, Guild-Only.
*/
STORE(6, 0, true),
/**
* TODO docs | https://discord.com/developers/docs/resources/channel#channel-object-channel-types
*/
GUILD_NEWS_THREAD(10, -1,true), //TODO confirm handling of sort bucket.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like you can't sort threads so this sort bucket value is irrelevant

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What even is a news thread anyway?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
GUILD_NEWS_THREAD(10, -1,true), //TODO confirm handling of sort bucket.
GUILD_NEWS_THREAD(10, -1, true),

GUILD_PUBLIC_THREAD(11, -1, true),
GUILD_PRIVATE_THREAD(12, -1, true),
/**
* Unknown Discord channel type. Should never happen and would only possibly happen if Discord implemented a new
* channel type and JDA had yet to implement support for it.
Expand Down
146 changes: 146 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/GuildThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.dv8tion.jda.api.entities;

import net.dv8tion.jda.api.utils.MiscUtil;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.OffsetDateTime;
import java.util.FormattableFlags;
import java.util.Formatter;
import java.util.List;

//TODO Cant actually use GuildChannel because of the significant differences between real channels and threads.
//TODO Just using it to get something working first.
public interface GuildThread extends GuildChannel, MessageChannel
{
//Stuff needed from GuildChannel
// - Interface: Mentionable
// - getJDA
// - getName
// - getGuild
// - getChannelType
// - getManager //TODO will need to be ThreadManager, not ChannelManager
// - getMembers //TODO might not need this as we'll have getThreadMembers()

//TODO pick a better name (or use getParent once we break from GuildChannel interface?)
GuildChannel getParentChannel();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just MessageChannel getAncestor() for now, you can only create threads on message channels anyway.


//TODO docs | Max returned amount is capped at 50 regardless of actual count
int getMessageCount();

//TODO docs | Max returned amount is capped at 50 regardless of actual count
int getMemberCount();

//TODO | This name is bad. Looking for alternatives.
boolean isSubscribedToThread();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about isJoined()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isJoined sounds, odd to me.

isSubscribed or hasJoined sound better imo


@Nullable
GuildThreadMember getSelfThreadMember();

//Only have access to this with GUILD_MEMBERS
List<GuildThreadMember> getThreadMembers();

default GuildThreadMember getThreadMember(Member member)
{
return getThreadMemberById(member.getId());
}

default GuildThreadMember getThreadMember(User user)
{
return getThreadMemberById(user.getId());
}

default GuildThreadMember getThreadMemberById(String id)
{
return getThreadMemberById(MiscUtil.parseSnowflake(id));
}

GuildThreadMember getThreadMemberById(long id);

//Should we provide a getter for the ThreadMember of the thread owner?
Member getOwner();

String getOwnerId();

long getOwnerIdLong();

boolean isArchived();

//TODO This name sucks.
OffsetDateTime getTimeArchive();

@Nullable
Member getArchivingMember();

@Nullable
String getArchivingMemberId();

long getArchivingMemberIdLong();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I receive a THREAD_UPDATE event it doesn't include the member who archived the channel. (I've the GUILD_MEMBERS intent)
Am I misunderstanding what this method is supposed to do or?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You would get this information regardless of the GUILD_MEMBERS intent. However, this property can be null (for the String version) or 0 (for the long version) when the thread was auto-archived by Discord based on inactivity duration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
  "op" : 0,
  "s" : 8,
  "t" : "THREAD_UPDATE",
  "d" : {
    "last_message_id" : "860587045402968105",
    "audience" : {
      "parent_id" : "859388924278734868",
      "channel_type" : 11
    },
    "parent_id" : "859388924278734868",
    "owner_id" : "823709352509833256",
    "name" : "wa",
    "guild_id" : "859165430752870400",
    "thread_metadata" : {
      "archived" : true,
      "archive_timestamp" : "2021-07-02T18:25:43.757579+00:00",
      "locked" : true,
      "auto_archive_duration" : 1440
    },
    "id" : "859390106968129587",
    "type" : 11,
    "member_count" : 2,
    "message_count" : 8
  }

When manually archiving a thread, am I doing something wrong or missing something? (using latest features/threads branch)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DV8FromTheWorld I'm probably doing something wrong, but just bumping it to make sure.


//TODO We should consider making this an enum. The only allowed values are: 60(1 hour), 1440(24 hours), 4320(3 days), 10080(1 week)
AutoArchiveDuration getAutoArchiveDuration();

@Override
default void formatTo(Formatter formatter, int flags, int width, int precision)
{
boolean leftJustified = (flags & FormattableFlags.LEFT_JUSTIFY) == FormattableFlags.LEFT_JUSTIFY;
boolean upper = (flags & FormattableFlags.UPPERCASE) == FormattableFlags.UPPERCASE;
boolean alt = (flags & FormattableFlags.ALTERNATE) == FormattableFlags.ALTERNATE;
String out;

if (alt)
out = "#" + (upper ? getName().toUpperCase(formatter.locale()) : getName());
else
out = getAsMention();

MiscUtil.appendTo(formatter, width, precision, leftJustified, out);
}

//////////////////////////

enum AutoArchiveDuration {
//TODO: I dislike this naming scheme. Need to come up with something better.
TIME_1_HOUR(60),
TIME_24_HOURS(1440),
TIME_3_DAYS(4320),
TIME_1_WEEK(10080);

private final int minutes;

AutoArchiveDuration(int minutes)
{
this.minutes = minutes;
}

public int getMinutes()
{
return minutes;
}

@Nonnull
public static AutoArchiveDuration fromKey(int minutes)
{
for (AutoArchiveDuration duration : values())
{
if (duration.getMinutes() == minutes)
return duration;
}
throw new IllegalArgumentException("Provided key was not recognized. Minutes: " + minutes);
}
}
}
28 changes: 28 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/GuildThreadMember.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package net.dv8tion.jda.api.entities;

import net.dv8tion.jda.api.JDA;

import javax.annotation.Nonnull;
import java.time.OffsetDateTime;

public interface GuildThreadMember
{
@Nonnull
JDA getJDA();

@Nonnull
GuildThread getThread();

Guild getGuild();

Member getMember();

User getUser();

@Nonnull
OffsetDateTime getTimeJoined();

//TODO | Set<ThreadMemberFlags> getFlags();

long getFlagsRaw();
}
13 changes: 13 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/MessageType.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ public enum MessageType

/**
* Specialized messages used for Groups as a System-Message showing that a new User has been added to the Group.
* Also used to indicate that a member has been added to a TODO {Thread}.
*/
RECIPIENT_ADD(1),

/**
* Specialized messages used for Groups as a System-Message showing that a new User has been removed from the Group.
* Also used to indicate that a member has been removed from a TODO {Thread}.
*/
RECIPIENT_REMOVE(2),

Expand All @@ -45,6 +47,7 @@ public enum MessageType

/**
* Specialized message used for Groups as a System-Message showing that the name of the Group was changed.
* Also used to indicate when a TODO {Thread} was renamed.
*/
CHANNEL_NAME_CHANGE(4),

Expand Down Expand Up @@ -108,6 +111,11 @@ public enum MessageType
*/
GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING(17),

/**
* TODO docs | https://discord.com/developers/docs/topics/threads#new-message-types
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* TODO docs | https://discord.com/developers/docs/topics/threads#new-message-types
* Message which indicates that a {@link net.dv8tion.jda.api.entities.GuildThread GuildThread} has been created.

*/
THREAD_CREATED(18),

/**
* Reply to another message. This usually comes with a {@link Message#getReferencedMessage() referenced message}.
*/
Expand All @@ -119,6 +127,11 @@ public enum MessageType
*/
APPLICATION_COMMAND(20, false),

/**
* TODO docs | THREAD_STARTER_MESSAGE
*/
THREAD_STARTER_MESSAGE(21),

/**
* Unknown MessageType.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ public ChannelType getChannelType()
@Nonnull
public Guild getGuild()
{
return getTextChannel().getGuild();
if (!(channel instanceof GuildChannel))
{
throw new IllegalStateException("This message was not sent in a guild");
}
Comment on lines +135 to +137
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{
throw new IllegalStateException("This message was not sent in a guild");
}
throw new IllegalStateException("This message was not sent in a guild");


//TODO | This will break when GuildThread doesn't extend GuildChannel
return ((GuildChannel) channel).getGuild();
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/net/dv8tion/jda/internal/JDAImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import net.dv8tion.jda.api.audio.factory.IAudioSendFactory;
import net.dv8tion.jda.api.audio.hooks.ConnectionStatus;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.GuildThread;
import net.dv8tion.jda.api.events.GatewayPingEvent;
import net.dv8tion.jda.api.events.GenericEvent;
import net.dv8tion.jda.api.events.StatusChangeEvent;
Expand Down Expand Up @@ -98,6 +99,7 @@ public class JDAImpl implements JDA
protected final SnowflakeCacheViewImpl<StoreChannel> storeChannelCache = new SnowflakeCacheViewImpl<>(StoreChannel.class, GuildChannel::getName);
protected final SnowflakeCacheViewImpl<TextChannel> textChannelCache = new SnowflakeCacheViewImpl<>(TextChannel.class, GuildChannel::getName);
protected final SnowflakeCacheViewImpl<VoiceChannel> voiceChannelCache = new SnowflakeCacheViewImpl<>(VoiceChannel.class, GuildChannel::getName);
protected final SnowflakeCacheViewImpl<GuildThread> guildThreadsCache = new SnowflakeCacheViewImpl<>(GuildThread.class, GuildThread::getName);
protected final SnowflakeCacheViewImpl<PrivateChannel> privateChannelCache = new SnowflakeCacheViewImpl<>(PrivateChannel.class, MessageChannel::getName);
protected final LinkedList<Long> privateChannelLRU = new LinkedList<>();

Expand Down Expand Up @@ -1088,6 +1090,8 @@ public SnowflakeCacheViewImpl<VoiceChannel> getVoiceChannelsView()
return voiceChannelCache;
}

public SnowflakeCacheViewImpl<GuildThread> getGuildThreadsView() { return guildThreadsCache; }

public SnowflakeCacheViewImpl<PrivateChannel> getPrivateChannelsView()
{
return privateChannelCache;
Expand Down
Loading