Breaking changes:

'Gift': changed from class to enum, so now you can handle
incoming gifts in switch

`Events`
- new:
     onGiftComboFinished
- Removed:
      onGiftBrodcast
- Rename:
     onGiftMessage -> onGift
     onRoomPinMessage -> onRoomPin
     onRoomMessage -> onRoom
     onLinkMessage -> onLink
     onBarrageMessage -> onBarrage
     onPollMessage -> onPoll
     onShopMessage -> onShop
     onDetectMessage -> onDetect

`GiftManager`
   added:
      registerGift
      findById
      findByName
      getGifts
   removed:
      getActiveGifts
This commit is contained in:
JW
2023-10-05 02:25:10 +02:00
parent e76703eae6
commit f55cbcae7e
106 changed files with 75409 additions and 3191 deletions

View File

@@ -32,7 +32,7 @@ import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
import io.github.jwdeveloper.tiktok.live.ConnectionState;
import io.github.jwdeveloper.tiktok.models.ConnectionState;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;

View File

@@ -26,6 +26,14 @@ import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.TikTokEventBuilder;
import io.github.jwdeveloper.tiktok.events.TikTokEventConsumer;
import io.github.jwdeveloper.tiktok.events.messages.*;
import io.github.jwdeveloper.tiktok.events.messages.TikTokConnectedEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokGiftComboFinishedEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokJoinEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokBarrageEvent;
import io.github.jwdeveloper.tiktok.events.messages.poll.TikTokPollEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;

View File

@@ -22,13 +22,16 @@
*/
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.live.ConnectionState;
import io.github.jwdeveloper.tiktok.models.ConnectionState;
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
import lombok.Data;
@Data
public class TikTokRoomInfo implements LiveRoomInfo
{
private int likesCount;
private int viewersCount;
private String roomId;

View File

@@ -23,12 +23,9 @@
package io.github.jwdeveloper.tiktok.gifts;
import io.github.jwdeveloper.tiktok.events.objects.Gift;
import io.github.jwdeveloper.tiktok.events.objects.TikTokGift;
import io.github.jwdeveloper.tiktok.events.objects.Picture;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.messages.WebcastGiftMessage;
import io.github.jwdeveloper.tiktok.models.GiftId;
import lombok.Getter;
import sun.misc.Unsafe;
import java.util.HashMap;
@@ -37,13 +34,10 @@ import java.util.Map;
public class TikTokGiftManager implements GiftManager {
@Getter
private final Map<GiftId, TikTokGift> activeGifts;
private final Map<Integer, Gift> indexById;
private final Map<String, Gift> indexByName;
public TikTokGiftManager() {
activeGifts = new HashMap<>();
indexById = new HashMap<>();
indexByName = new HashMap<>();
init();
@@ -56,26 +50,7 @@ public class TikTokGiftManager implements GiftManager {
}
}
public TikTokGift updateActiveGift(WebcastGiftMessage giftMessage) {
var giftId = new GiftId(giftMessage.getGiftId(), giftMessage.getUser().getIdStr());
if (activeGifts.containsKey(giftId)) {
var gift = activeGifts.get(giftId);
gift.setAmount(giftMessage.getComboCount());
} else {
var newGift = new TikTokGift(findById((int) giftMessage.getGiftId()), giftMessage);
activeGifts.put(giftId, newGift);
}
var gift = activeGifts.get(giftId);
if (giftMessage.getRepeatEnd() > 0) {
gift.setStreakFinished(true);
activeGifts.remove(giftId);
}
return gift;
}
public Gift registerGift(int id, String name, int diamondCost) {
public Gift registerGift(int id, String name, int diamondCost, Picture picture) {
try {
var constructor = Unsafe.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
@@ -95,6 +70,10 @@ public class TikTokGiftManager implements GiftManager {
field.setAccessible(true);
field.set(enumInstance, diamondCost);
field = Gift.class.getDeclaredField("picture");
field.setAccessible(true);
field.set(enumInstance, picture);
indexById.put(enumInstance.getId(), enumInstance);
indexByName.put(enumInstance.getName(), enumInstance);

View File

@@ -31,7 +31,7 @@ import io.github.jwdeveloper.tiktok.events.messages.TikTokWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokUnhandledWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokMessageMappingException;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import java.util.Arrays;
import java.util.HashMap;
@@ -75,11 +75,11 @@ public abstract class TikTokMessageHandler {
public void handleSingleMessage(TikTokLiveClient client, WebcastResponse.Message message) throws Exception {
if (!handlers.containsKey(message.getType())) {
if (!handlers.containsKey(message.getMethod())) {
tikTokEventHandler.publish(client, new TikTokUnhandledWebsocketMessageEvent(message));
return;
}
var handler = handlers.get(message.getType());
var handler = handlers.get(message.getMethod());
var tiktokEvent = handler.handle(message);
tikTokEventHandler.publish(client, new TikTokWebsocketMessageEvent(tiktokEvent, message));
tikTokEventHandler.publish(client, tiktokEvent);
@@ -88,7 +88,7 @@ public abstract class TikTokMessageHandler {
protected TikTokEvent mapMessageToEvent(Class<?> inputClazz, Class<?> outputClass, WebcastResponse.Message message) {
try {
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", ByteString.class);
var deserialized = parseMethod.invoke(null, message.getBinary());
var deserialized = parseMethod.invoke(null, message.getPayload());
var constructors = Arrays.stream(outputClass.getConstructors())
.filter(ea -> Arrays.stream(ea.getParameterTypes())

View File

@@ -25,9 +25,16 @@ package io.github.jwdeveloper.tiktok.handlers;
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.messages.*;
import io.github.jwdeveloper.tiktok.events.messages.TikTokBarrageEvent;
import io.github.jwdeveloper.tiktok.events.messages.poll.TikTokPollEndEvent;
import io.github.jwdeveloper.tiktok.events.messages.poll.TikTokPollEvent;
import io.github.jwdeveloper.tiktok.events.messages.poll.TikTokPollStartEvent;
import io.github.jwdeveloper.tiktok.events.messages.poll.TikTokPollUpdateEvent;
import io.github.jwdeveloper.tiktok.events.objects.Gift;
import io.github.jwdeveloper.tiktok.events.objects.Picture;
import io.github.jwdeveloper.tiktok.events.objects.Text;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.messages.*;
import io.github.jwdeveloper.tiktok.messages.webcast.*;
import io.github.jwdeveloper.tiktok.models.SocialTypes;
import lombok.SneakyThrows;
@@ -36,7 +43,6 @@ import java.util.regex.Pattern;
public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
private final TikTokGiftManager giftManager;
private final TikTokRoomInfo roomInfo;
private final Pattern socialMediaPattern = Pattern.compile("pm_mt_guidance_viewer_([0-9]+)_share");
public TikTokMessageHandlerRegistration(TikTokEventObserver tikTokEventHandler,
@@ -52,32 +58,30 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
//ConnectionEvents events
registerMapping(WebcastControlMessage.class, this::handleWebcastControlMessage);
registerMapping(SystemMessage.class, TikTokRoomEvent.class);
registerMapping(WebcastSystemMessage.class, TikTokRoomEvent.class);
//Room status events
registerMapping(WebcastLiveIntroMessage.class, TikTokRoomEvent.class);
registerMapping(WebcastRoomUserSeqMessage.class, this::handleRoomUserSeqMessage);
registerMapping(RoomMessage.class, TikTokRoomEvent.class);
registerMapping(WebcastRoomMessage.class, TikTokRoomEvent.class);
registerMapping(WebcastCaptionMessage.class, TikTokCaptionEvent.class);
//User Interactions events
registerMapping(WebcastChatMessage.class, TikTokCommentEvent.class);
registerMapping(WebcastLikeMessage.class, TikTokLikeEvent.class);
registerMapping(WebcastLikeMessage.class, this::handleLike);
registerMapping(WebcastGiftMessage.class, this::handleGift);
registerMapping(WebcastSocialMessage.class, this::handleSocialMedia);
registerMapping(WebcastMemberMessage.class, this::handleMemberMessage);
//Host Interaction events
registerMapping(WebcastPollMessage.class, TikTokPollEvent.class);
registerMapping(WebcastRoomPinMessage.class, TikTokRoomPinEvent.class);
registerMapping(WebcastPollMessage.class, this::handlePollEvent);
registerMapping(WebcastRoomPinMessage.class, this::handlePinMessage);
registerMapping(WebcastGoalUpdateMessage.class, TikTokGoalUpdateEvent.class);
//LinkMic events
registerMapping(WebcastLinkMicBattle.class, TikTokLinkMicBattleEvent.class);
registerMapping(WebcastLinkMicArmies.class, TikTokLinkMicArmiesEvent.class);
registerMapping(LinkMicMethod.class, TikTokLinkMicMethodEvent.class);
registerMapping(WebcastLinkMicMethod.class, TikTokLinkMicMethodEvent.class);
registerMapping(WebcastLinkMicFanTicketMethod.class, TikTokLinkMicFanTicketEvent.class);
@@ -102,7 +106,7 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
@SneakyThrows
private TikTokEvent handleWebcastControlMessage(WebcastResponse.Message msg) {
var message = WebcastControlMessage.parseFrom(msg.getBinary());
var message = WebcastControlMessage.parseFrom(msg.getPayload());
return switch (message.getAction()) {
case STREAM_PAUSED -> new TikTokLivePausedEvent();
case STREAM_ENDED -> new TikTokLiveEndedEvent();
@@ -112,8 +116,8 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
@SneakyThrows
private TikTokEvent handleGift(WebcastResponse.Message msg) {
var giftMessage = WebcastGiftMessage.parseFrom(msg.getBinary());
giftManager.updateActiveGift(giftMessage);
var giftMessage = WebcastGiftMessage.parseFrom(msg.getPayload());
var gift = giftManager.findById((int) giftMessage.getGiftId());
if (gift == Gift.UNDEFINED) {
@@ -123,7 +127,8 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
gift = giftManager.registerGift(
(int) giftMessage.getGift().getId(),
giftMessage.getGift().getName(),
giftMessage.getGift().getDiamondCount());
giftMessage.getGift().getDiamondCount(),
Picture.Map(giftMessage.getGift().getImage()));
}
if (giftMessage.getRepeatEnd() > 0) {
@@ -135,9 +140,9 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
@SneakyThrows
private TikTokEvent handleSocialMedia(WebcastResponse.Message msg) {
var message = WebcastSocialMessage.parseFrom(msg.getBinary());
var message = WebcastSocialMessage.parseFrom(msg.getPayload());
var socialType = message.getHeader().getSocialData().getType();
var socialType = Text.map(message.getCommon().getDisplayText()).getKey();
var matcher = socialMediaPattern.matcher(socialType);
if (matcher.find()) {
@@ -147,17 +152,17 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
}
return switch (socialType) {
case SocialTypes.LikeType -> new TikTokLikeEvent(message);
case SocialTypes.LikeType -> new TikTokLikeEvent(message, roomInfo.getLikesCount());
case SocialTypes.FollowType -> new TikTokFollowEvent(message);
case SocialTypes.ShareType -> new TikTokShareEvent(message);
case SocialTypes.JoinType -> new TikTokJoinEvent(message);
case SocialTypes.JoinType -> new TikTokJoinEvent(message, roomInfo.getViewersCount());
default -> new TikTokUnhandledSocialEvent(message);
};
}
@SneakyThrows
private TikTokEvent handleMemberMessage(WebcastResponse.Message msg) {
var message = WebcastMemberMessage.parseFrom(msg.getBinary());
var message = WebcastMemberMessage.parseFrom(msg.getPayload());
return switch (message.getAction()) {
case JOINED -> new TikTokJoinEvent(message);
case SUBSCRIBED -> new TikTokSubscribeEvent(message);
@@ -170,4 +175,32 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
roomInfo.setViewersCount(event.getViewerCount());
return event;
}
private TikTokEvent handleLike(WebcastResponse.Message msg) {
var event = (TikTokLikeEvent) mapMessageToEvent(WebcastLikeMessage.class, TikTokLikeEvent.class, msg);
roomInfo.setLikesCount(event.getTotalLikes());
return event;
}
@SneakyThrows
private TikTokEvent handlePinMessage(WebcastResponse.Message msg) {
var pinMessage = WebcastRoomPinMessage.parseFrom(msg.getPayload());
var chatMessage = WebcastChatMessage.parseFrom(pinMessage.getPinnedMessage());
var chatEvent = new TikTokCommentEvent(chatMessage);
return new TikTokRoomPinEvent(pinMessage, chatEvent);
}
//TODO check
@SneakyThrows
private TikTokEvent handlePollEvent(WebcastResponse.Message msg) {
var poolMessage = WebcastPollMessage.parseFrom(msg.getPayload());
return switch (poolMessage.getMessageType()) {
case 0 -> new TikTokPollStartEvent(poolMessage);
case 1 -> new TikTokPollEndEvent(poolMessage);
case 2 -> new TikTokPollUpdateEvent(poolMessage);
default -> new TikTokPollEvent(poolMessage);
};
}
}

View File

@@ -22,20 +22,16 @@
*/
package io.github.jwdeveloper.tiktok.http;
import com.google.gson.Gson;
import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
import io.github.jwdeveloper.tiktok.mappers.LiveRoomMetaMapper;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import io.github.jwdeveloper.tiktok.models.gifts.TikTokGiftInfo;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TikTokApiService {
@@ -137,34 +133,10 @@ public class TikTokApiService {
try {
var response = tiktokHttpClient.getDeserializedMessage("im/fetch/", clientSettings.getClientParameters());
clientSettings.getClientParameters().put("cursor", response.getCursor());
clientSettings.getClientParameters().put("internal_ext", response.getAckIds());
clientSettings.getClientParameters().put("internal_ext", response.getInternalExt());
return response;
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch client data", e);
}
}
public Map<Integer, TikTokGiftInfo> fetchAvailableGifts() {
try {
var response = tiktokHttpClient.getJObjectFromWebcastAPI("gift/list/", clientSettings.getClientParameters());
if (!response.has("data")) {
return new HashMap<>();
}
var dataJson = response.getAsJsonObject("data");
if (!dataJson.has("gifts")) {
return new HashMap<>();
}
var giftsJsonList = dataJson.get("gifts").getAsJsonArray();
var gifts = new HashMap<Integer, TikTokGiftInfo>();
var gson = new Gson();
for (var jsonGift : giftsJsonList) {
var gift = gson.fromJson(jsonGift, TikTokGiftInfo.class);
logger.info("Found Gift " + gift.getName() + " with ID " + gift.getId());
gifts.put(gift.getId(), gift);
}
return gifts;
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch giftTokens from WebCast, see stacktrace for more info.", e);
}
}
}

View File

@@ -26,7 +26,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.github.jwdeveloper.tiktok.Constants;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import java.net.URI;
import java.net.http.HttpClient;

View File

@@ -31,7 +31,7 @@ import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.http.HttpUtils;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import org.java_websocket.client.WebSocketClient;
import java.net.URI;
@@ -67,10 +67,11 @@ public class TikTokWebSocketClient {
stop();
}
if (webcastResponse.getSocketUrl().isEmpty() ||
webcastResponse.getSocketParamsList().isEmpty()) {
if (webcastResponse.getPushServer().isEmpty() ||
webcastResponse.getRouteParamsMapMap().isEmpty()) {
throw new TikTokLiveException("Could not find Room");
}
try {
var url = getWebSocketUrl(webcastResponse);
if (clientSettings.isHandleExistingEvents()) {
@@ -102,16 +103,10 @@ public class TikTokWebSocketClient {
}
private String getWebSocketUrl(WebcastResponse webcastResponse) {
var params = webcastResponse.getSocketParamsList().get(0);
var name = params.getName();
var value = params.getValue();
var headers = Constants.DefaultRequestHeaders();
var clone = new TreeMap<>(clientSettings.getClientParameters());
clone.putAll(headers);
clone.put(name, value);
var url = webcastResponse.getSocketUrl();
clone.putAll(Constants.DefaultRequestHeaders());
clone.putAll(webcastResponse.getRouteParamsMapMap());
var url = webcastResponse.getPushServer();
return HttpUtils.parseParametersEncode(url, clone);
}

View File

@@ -30,9 +30,9 @@ import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketAck;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastPushFrame;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastWebsocketAck;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
@@ -105,16 +105,16 @@ public class TikTokWebSocketListener extends WebSocketClient {
return;
}
var websocketMessage = websocketMessageOptional.get();
sendAckId(websocketMessage.getId());
sendAckId(websocketMessage.getSeqId());
var webResponse = getWebResponseMessage(websocketMessage.getBinary());
var webResponse = getWebResponseMessage(websocketMessage.getPayload());
webResponseHandler.handle(tikTokLiveClient, webResponse);
}
private Optional<WebcastWebsocketMessage> getWebcastWebsocketMessage(byte[] buffer) {
private Optional<WebcastPushFrame> getWebcastWebsocketMessage(byte[] buffer) {
try {
var websocketMessage = WebcastWebsocketMessage.parseFrom(buffer);
if (websocketMessage.getBinary().isEmpty()) {
var websocketMessage = WebcastPushFrame.parseFrom(buffer);
if (websocketMessage.getPayload().isEmpty()) {
return Optional.empty();
}
return Optional.of(websocketMessage);

View File

@@ -22,8 +22,7 @@
*/
package io.github.jwdeveloper.tiktok.common;
import com.google.protobuf.InvalidProtocolBufferException;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage;
import java.io.IOException;
import java.util.Base64;

View File

@@ -23,7 +23,7 @@
package io.github.jwdeveloper.tiktok.gifts;
import io.github.jwdeveloper.tiktok.events.objects.Gift;
import org.junit.Assert;
import io.github.jwdeveloper.tiktok.events.objects.Picture;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -33,40 +33,37 @@ import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class TikTokGiftManagerTest
{
public class TikTokGiftManagerTest {
@InjectMocks
TikTokGiftManager giftManager;
private static final Picture rosePicture = new Picture("https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/eba3a9bb85c33e017f3648eaf88d7189~tplv-obj.png");
@Test
void registerGift()
{
var fakeGift = giftManager.registerGift(123,"Fake gift",123123);
void registerGift() {
var fakeGift = giftManager.registerGift(123, "Fake gift", 123123, rosePicture);
var gifts = giftManager.getGifts();
var optional = gifts.stream().filter(r -> r == fakeGift).findFirst();
Assertions.assertTrue(optional.isPresent());
}
@Test
void findById()
{
var target = giftManager.registerGift(123,"FAKE",123123);
void findById() {
var target = giftManager.registerGift(123, "FAKE", 123123, rosePicture);
var result = giftManager.findById(target.getId());
Assertions.assertEquals(target,result);
Assertions.assertEquals(target, result);
}
@Test
void findByName()
{
var target = giftManager.registerGift(123,"FAKE",123123);
var result = giftManager.findByName(target.getName());
Assertions.assertEquals(target,result);
void findByName() {
var target = giftManager.registerGift(123, "FAKE", 123123, rosePicture);
var result = giftManager.findByName(target.getName());
Assertions.assertEquals(target, result);
}
@Test
void getGifts()
{
void getGifts() {
Assertions.assertEquals(Gift.values().length, giftManager.getGifts().size());
}