Import bookmarks and identities from the TeamSpeak 3 client
The official client keeps what a myTeamSpeak account synchronises as protobuf Item_Data blobs in the ProtobufItems table of settings.db. Decode them, adopt them on first run when we have no identity yet, and offer a re-import from Manage Bookmarks and Identities. The settings.db reader moves from contacts to a new teamspeak package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package com.ts3client.teamspeak;
|
||||
|
||||
import com.ts3client.config.IdentityStore;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* The fixture is the {@code ProtobufItems} table a fresh official client wrote
|
||||
* (config, hotkey profile, "Default" identity, "TeamSpeak Public" bookmark,
|
||||
* checksum), plus a folder, two more bookmarks, a deleted bookmark and a
|
||||
* whisper list appended in the same encoding.
|
||||
*/
|
||||
class SyncItemsTest {
|
||||
|
||||
private static final String IDENTITY_UUID = "965483cf-210f-dd22-ee1f-33591b0587a4";
|
||||
private static final String FOLDER_UUID = "0f0f0f0f-0000-4000-8000-000000000001";
|
||||
|
||||
static File fixture() {
|
||||
return new File(SyncItemsTest.class.getResource("/teamspeak/sync.db").getFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
void readsBookmarksIdentitiesAndFoldersOnly() throws Exception {
|
||||
List<SyncItem> items = TeamSpeakSettingsDb.readSyncItems(fixture());
|
||||
assertEquals(5, items.size());
|
||||
assertEquals(List.of(IDENTITY_UUID, "ea120ce1-b68b-b314-46ee-e16b3bf10342", FOLDER_UUID,
|
||||
"bbbbbbbb-0000-4000-8000-000000000002", "bbbbbbbb-0000-4000-8000-000000000003"),
|
||||
items.stream().map(SyncItem::uuid).toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodesTheClientsOwnIdentityAndBookmark() throws Exception {
|
||||
List<SyncItem> items = TeamSpeakSettingsDb.readSyncItems(fixture());
|
||||
|
||||
SyncItem.Identity identity = (SyncItem.Identity) items.get(0);
|
||||
assertEquals("Default", identity.name());
|
||||
assertEquals("SyncTester", identity.nickname());
|
||||
assertEquals("", identity.parentUuid());
|
||||
assertTrue(identity.isDefault());
|
||||
assertTrue(identity.identity().matches("\\d+V[\\w+/=]+"));
|
||||
assertNotNull(IdentityStore.parseIdentityString(identity.identity()).getUid());
|
||||
|
||||
SyncItem.Bookmark bookmark = (SyncItem.Bookmark) items.get(1);
|
||||
assertEquals("TeamSpeak Public", bookmark.name());
|
||||
assertEquals("voice.teamspeak.com", bookmark.address());
|
||||
assertEquals(9987, bookmark.port());
|
||||
assertEquals("", bookmark.identityUuid(), "connects with the default identity");
|
||||
assertEquals("", bookmark.nickname());
|
||||
assertFalse(bookmark.autoconnect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodesFoldersAndEveryBookmarkField() throws Exception {
|
||||
List<SyncItem> items = TeamSpeakSettingsDb.readSyncItems(fixture());
|
||||
|
||||
SyncItem.Folder folder = (SyncItem.Folder) items.get(2);
|
||||
assertEquals("Clan servers", folder.name());
|
||||
|
||||
SyncItem.Bookmark inFolder = (SyncItem.Bookmark) items.get(3);
|
||||
assertEquals(FOLDER_UUID, inFolder.parentUuid());
|
||||
assertEquals("Lixko", inFolder.name());
|
||||
assertEquals(IDENTITY_UUID, inFolder.identityUuid());
|
||||
assertEquals("server.lixko.eu", inFolder.address());
|
||||
assertEquals("Bookmark Nick", inFolder.nickname());
|
||||
assertEquals("hunter2", inFolder.serverPassword());
|
||||
assertEquals("Lobby/Games", inFolder.defaultChannel());
|
||||
assertEquals("chanpw", inFolder.defaultChannelPassword());
|
||||
assertTrue(inFolder.autoconnect());
|
||||
|
||||
SyncItem.Bookmark byId = (SyncItem.Bookmark) items.get(4);
|
||||
assertEquals(9988, byId.port());
|
||||
assertEquals(42, byId.defaultChannelId());
|
||||
assertEquals("", byId.identityUuid());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.ts3client.teamspeak;
|
||||
|
||||
import com.ts3client.config.Bookmark;
|
||||
import com.ts3client.config.Bookmarks;
|
||||
import com.ts3client.config.IdentityEntry;
|
||||
import com.ts3client.config.IdentityStore;
|
||||
import com.ts3client.config.Settings;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class TeamSpeakImporterTest {
|
||||
|
||||
/** The stores resolve {@code ~/.ts3jclient} once, so point them at scratch space before they load. */
|
||||
@BeforeAll
|
||||
static void isolateHome() throws Exception {
|
||||
System.setProperty("user.home", Files.createTempDirectory("ts3j-import").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void importsBookmarksWithTheIdentitiesTheyUse() throws Exception {
|
||||
Settings settings = new Settings();
|
||||
settings.nickname = "Fallback";
|
||||
IdentityStore identities = IdentityStore.load(settings);
|
||||
Bookmarks bookmarks = new Bookmarks();
|
||||
bookmarks.add(new Bookmark("TeamSpeak Public", "VOICE.teamspeak.com", 9987, "me", ""));
|
||||
|
||||
TeamSpeakImporter importer = new TeamSpeakImporter(identities, bookmarks, settings);
|
||||
TeamSpeakImporter.Result result = importer.importBookmarks(SyncItemsTest.fixture());
|
||||
assertEquals(1, result.identitiesAdded());
|
||||
assertEquals(2, result.bookmarksAdded());
|
||||
assertEquals(1, result.bookmarksKnown());
|
||||
|
||||
IdentityEntry imported = identities.all().get(0);
|
||||
assertEquals("Default", imported.getName());
|
||||
assertEquals(imported.getId(), settings.defaultIdentityId);
|
||||
assertTrue(imported.getFile().isFile());
|
||||
|
||||
List<Bookmark> all = bookmarks.all();
|
||||
assertEquals(3, all.size());
|
||||
Bookmark lixko = all.get(1);
|
||||
assertEquals("Lixko", lixko.label);
|
||||
assertEquals("Bookmark Nick", lixko.nickname);
|
||||
assertEquals("hunter2", lixko.password);
|
||||
assertEquals("Lobby/Games", lixko.channel);
|
||||
assertEquals("chanpw", lixko.channelPassword);
|
||||
assertEquals(imported.getId(), lixko.identityId);
|
||||
assertTrue(lixko.connectOnStartup);
|
||||
|
||||
Bookmark byId = all.get(2);
|
||||
assertEquals("/42", byId.channel);
|
||||
assertEquals("SyncTester", byId.nickname, "the default identity's nickname");
|
||||
assertEquals("", byId.identityId, "connects with whatever the default identity is");
|
||||
assertEquals("SyncTester", settings.nickname);
|
||||
|
||||
// Running it again changes nothing.
|
||||
result = importer.importBookmarks(SyncItemsTest.fixture());
|
||||
assertEquals(0, result.identitiesAdded());
|
||||
assertEquals(0, result.bookmarksAdded());
|
||||
assertEquals(3, bookmarks.all().size());
|
||||
assertEquals(1, identities.all().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void identityImportRecognisesAKnownKey() throws Exception {
|
||||
Settings settings = new Settings();
|
||||
IdentityStore identities = IdentityStore.load(settings);
|
||||
int before = identities.all().size();
|
||||
TeamSpeakImporter importer = new TeamSpeakImporter(identities, new Bookmarks(), settings);
|
||||
|
||||
TeamSpeakImporter.Result first = importer.importIdentities(SyncItemsTest.fixture());
|
||||
TeamSpeakImporter.Result second = importer.importIdentities(SyncItemsTest.fixture());
|
||||
assertEquals(1, first.identities());
|
||||
assertEquals(1, second.identitiesKnown());
|
||||
assertEquals(before + first.identitiesAdded(), identities.all().size());
|
||||
assertNotNull(identities.byId(settings.defaultIdentityId));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.ts3client.contacts;
|
||||
package com.ts3client.teamspeak;
|
||||
|
||||
import com.ts3client.contacts.Contact;
|
||||
import com.ts3client.contacts.ContactType;
|
||||
import com.ts3client.contacts.NicknameDisplay;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
@@ -12,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
class TeamSpeakSettingsDbTest {
|
||||
|
||||
private static File fixture() {
|
||||
return new File(TeamSpeakSettingsDbTest.class.getResource("/contacts/settings.db").getFile());
|
||||
return new File(TeamSpeakSettingsDbTest.class.getResource("/teamspeak/settings.db").getFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
BIN
ts3-client/core/src/test/resources/teamspeak/sync.db
Normal file
BIN
ts3-client/core/src/test/resources/teamspeak/sync.db
Normal file
Binary file not shown.
Reference in New Issue
Block a user