На мой взгляд, кроме использования Evolution есть два способа импортировать контакты. Без Evolution для этого не существует автоматического инструмента.
Сделайте резервную копию ваших контактов.db перед внесением каких-либо изменений!
Первый способ — это простой способ импорта контактов с помощью folks-import
. Я проверил источник, и, похоже, это импортер из файла blist.xml
пиджина.
blist.xml
- A local copy of your buddy lists, for use in keeping locally applied aliases and group and buddy ordering between. I have tried it, out of curiosity, on my blist.xml
and it worked for some contacts.
Для вас создать blist.xml
(вот blist.h и источник для него), вы можете использовать чью-то работу, например vcftoxml.py и настройте его.
Изменить — попытка создать фиктивный XML-файл для импорта
Я попытался создать простой xml (blist.xml) для импорта через импортер.
XML-код, вдохновленный примером blist.xml
:
<?xml version="1.0" encoding="UTF-8" ?>
<purple version="1.0">
<blist>
<group name='GroupName'>
<contact>
<buddy account='test' proto='prpl-hangouts'>
<name>1131313213446</name>
<alias>Jakto Tomos</alias>
<phone>+6568309312</phone>
<email>[email protected]</email>
<address>Berlin</address>
</buddy>
</contact>
</group>
</blist>
<privacy>
</privacy>
</purple>
Когда я проверил созданный контакт, я обнаружил, что было импортировано только имя alias
.
Итак, я создал blist.xml
, который имитирует реальный:
<?xml version="1.0" encoding="UTF-8" ?>
<purple version="1.0">
<blist>
<group name='GroupName'>
<contact>
<buddy account='test' proto='prpl-hangouts'>
<name>1131313213446</name>
<alias>Jakto Tomos</alias>
<setting name='phone' type='string'>+6568309312</setting>
<setting name='email' type='string'>[email protected]</setting>
<setting name='address' type='string'>Lesser strasse, Berlin</setting>
</buddy>
</contact>
</group>
</blist>
<privacy>
</privacy>
</purple>
Когда я снова импортировал его, я снова получил только имя alias
. Итак, я перешел к исходному коду import-pidgin.vala
и обнаружил, что он действительно импортирует только имя (contact ID
) и псевдоним в качестве имени:
...
/* Parse the <name> and <alias> elements beneath <buddy> */
for (Xml.Node *subiter = iter->children; subiter != null;
subiter = subiter->next)
{
if (subiter->type != ElementType.ELEMENT_NODE)
continue;
if (subiter->name == "alias")
alias = subiter->get_content ();
else if (subiter->name == "name")
{
/* The <name> element seems to give the contact ID, which
* we need to insert into the Persona's im-addresses property
* for the linking to work. */
string im_address = subiter->get_content ();
im_addresses.set (tp_protocol,
new ImFieldDetails (im_address));
im_address_string += " %s\n".printf (im_address);
}
}
...
Таким образом, этот способ возможен, но вы должны добавить синтаксический анализ узлов, которые хотите импортировать. Вы можете сделать PR
для такого патча, чтобы другие после вас могли его использовать (я рекомендую использовать нотацию <setting name...
, так как именно так отформатирован файл blist.xml.
- Второй способ сложнее, и вам нужно правильно заполнить таблицы в sqlite. Это структура БД:
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "folder_id_phone_list" (
"uid" TEXT NOT NULL,
"value" TEXT,
FOREIGN KEY("uid") REFERENCES "folder_id"("uid")
);
CREATE TABLE IF NOT EXISTS "folder_id_email_list" (
"uid" TEXT NOT NULL,
"value" TEXT,
FOREIGN KEY("uid") REFERENCES "folder_id"("uid")
);
CREATE TABLE IF NOT EXISTS "folder_id" (
"uid" TEXT,
"Rev" TEXT,
"file_as" TEXT,
"file_as_localized" TEXT,
"nickname" TEXT,
"full_name" TEXT,
"given_name" TEXT,
"given_name_localized" TEXT,
"family_name" TEXT,
"family_name_localized" TEXT,
"is_list" INTEGER,
"list_show_addresses" INTEGER,
"wants_html" INTEGER,
"x509Cert" INTEGER,
"vcard" TEXT,
"bdata" TEXT,
PRIMARY KEY("uid")
);
CREATE TABLE IF NOT EXISTS "keys" (
"key" TEXT,
"value" TEXT,
"folder_id" TEXT,
PRIMARY KEY("key"),
FOREIGN KEY("folder_id") REFERENCES "folders"
);
CREATE TABLE IF NOT EXISTS "folders" (
"folder_id" TEXT,
"version" INTEGER,
"multivalues" TEXT,
"lc_collate" TEXT,
"countrycode" VARCHAR(2),
PRIMARY KEY("folder_id")
);
INSERT INTO "keys" VALUES ('eds-reserved-namespace-is-populated','1','folder_id');
INSERT INTO "keys" VALUES ('revision','2019-11-04T07:28:25Z(0)','folder_id');
INSERT INTO "folders" VALUES ('folder_id',11,'email;prefix:phone','en_US.UTF-8','US');
CREATE INDEX IF NOT EXISTS "UID_INDEX_phone_folder_id" ON "folder_id_phone_list" (
"uid"
);
CREATE INDEX IF NOT EXISTS "INDEX_email_folder_id" ON "folder_id_email_list" (
"value"
);
CREATE INDEX IF NOT EXISTS "UID_INDEX_email_folder_id" ON "folder_id_email_list" (
"uid"
);
CREATE INDEX IF NOT EXISTS "SINDEX_family_name_folder_id" ON "folder_id" (
"family_name_localized"
);
CREATE INDEX IF NOT EXISTS "INDEX_family_name_folder_id" ON "folder_id" (
"family_name"
);
CREATE INDEX IF NOT EXISTS "SINDEX_given_name_folder_id" ON "folder_id" (
"given_name_localized"
);
CREATE INDEX IF NOT EXISTS "INDEX_given_name_folder_id" ON "folder_id" (
"given_name"
);
CREATE INDEX IF NOT EXISTS "INDEX_full_name_folder_id" ON "folder_id" (
"full_name"
);
CREATE INDEX IF NOT EXISTS "INDEX_nickname_folder_id" ON "folder_id" (
"nickname"
);
CREATE INDEX IF NOT EXISTS "SINDEX_file_as_folder_id" ON "folder_id" (
"file_as_localized"
);
CREATE INDEX IF NOT EXISTS "INDEX_file_as_folder_id" ON "folder_id" (
"file_as"
);
CREATE INDEX IF NOT EXISTS "keysindex" ON "keys" (
"folder_id"
);
COMMIT;
Затем вам нужно будет создать кучу вставок, которые следуют логике структуры следующим образом:
INSERT INTO "folder_id_phone_list" VALUES ('pas-id-5DBFDF5A00000000','+0000000000');
INSERT INTO "folder_id_phone_list" VALUES ('pas-id-5DBFDF5A00000000','+131545678');
INSERT INTO "folder_id_phone_list" VALUES ('pas-id-5DBFE7F200000001','+45646546565465');
INSERT INTO "folder_id_email_list" VALUES ('pas-id-5DBFDF5A00000000','[email protected]');
INSERT INTO "folder_id_email_list" VALUES ('pas-id-5DBFDF5A00000000','[email protected]');
INSERT INTO "folder_id_email_list" VALUES ('pas-id-5DBFE7F200000001','[email protected]');
INSERT INTO "folder_id" VALUES ('pas-id-5DBFDF5A00000000','2019-11-04T08:28:52Z(15)','a header mail, this','001-)71)/1KA)9?O79M��',NULL,'john smith','this','020-O79M�','a header mail','001-)71)/1KA)9?',0,0,0,0,'BEGIN:VCARD
VERSION:3.0
UID:pas-id-5DBFDF5A00000000
X-URIS:www.testing.com
FN:John Smith
N:a header mail;This;is;;
X-EVOLUTION-FILE-AS:a header mail\, This
REV:2019-11-04T08:28:52Z(15)
TEL;TYPE=VOICE,HOME:+0000000000
TEL;TYPE=CELL:+131545678
EMAIL;TYPE=PERSONAL:[email protected]
EMAIL;TYPE=HOME:[email protected]
ADR;TYPE=WORK:p.o. box;456;Stree work;New York;New York;456465;USA
ADR;TYPE=HOME:p.o. box;456;Street;City;New York State;13676;USA
BDAY:2019-11-04
PHOTO;VALUE=uri:file:///home/osboxes/.local/share/evolution/addressbook/sys
tem/photos/pas_id_5DBFDF5A00000000_photo-file0.image%252Fpng
END:VCARD',NULL);
INSERT INTO "folder_id" VALUES ('pas-id-5DBFE7F200000001','2019-11-04T08:57:22Z(17)','sdaf','019-M/)3',NULL,'sdaf','sdaf','019-M/)3','','000-',0,0,0,0,'BEGIN:VCARD
VERSION:3.0
EMAIL;TYPE=PERSONAL:[email protected]
TEL;TYPE=CELL:+45646546565465
ADR;TYPE=HOME:asdf;;asdfsdfasdf;;sadf;;asdf
FN:sdaf
N:;sdaf;;;
X-EVOLUTION-FILE-AS:sdaf
UID:pas-id-5DBFE7F200000001
REV:2019-11-04T08:57:22Z(17)
END:VCARD',NULL);
INSERT INTO "folder_id" VALUES ('pas-id-5DBFE7F800000002','2019-11-04T08:57:28Z(19)','next','014-C1WO',NULL,'next','next','014-C1WO','','000-',0,0,0,0,'BEGIN:VCARD
VERSION:3.0
FN:next
N:;next;;;
X-EVOLUTION-FILE-AS:next
UID:pas-id-5DBFE7F800000002
REV:2019-11-04T08:57:28Z(19)
END:VCARD',NULL);
INSERT INTO "keys" VALUES ('eds-reserved-namespace-is-populated','1','folder_id');
INSERT INTO "keys" VALUES ('revision','2019-11-04T08:57:28Z(20)','folder_id');
INSERT INTO "folders" VALUES ('folder_id',11,'email;prefix:phone','en_US.UTF-8','US');
Важно отметить, что uid, например. для первой записи: pas-id-5DBFDF5A00000000
(которая определяется как new HashMap<string, Field?>
). Я вижу это как фиксированную строку pas-id-
, затем шестнадцатеричное число 5DBFDF5A
(появляется случайным) и последним является счетчик 00000000
(следующим будет 00000001
).
Лично я бы выбрал первый вариант, но второй есть, если у вас возникнут проблемы с созданием файла xml
. Если вы используете ручные вставки, не забудьте согласовать данные.
Редактировать – прокомментировать экспортированные данные
Похоже, что такие атрибуты, как file_as_localized
, given_name_localized
и family_name_localized
, содержат некоторые «мусорные» данные. Однако эти атрибуты определены как TEXT
, поэтому должны быть какие-то читаемые данные, а их нет (пробовал с другой кодировкой, но не помогло). Я рекомендую прочитать исходный код этих атрибутов. Возможно, есть возможность ввести пустое значение, как это делается на каком-то аккаунте в family_name_localized
только с 000-
. Вы также можете попробовать его со значением NULL в начале, и когда вы его узнаете, вы сможете обновить его позже.
Заключение
После выполнения тестов с помощью импортера я думаю, что более простым вариантом на самом деле является вариант 2. Вариант 1 будет включать в себя некоторое программирование в vala
, если вы не хотите импортировать только имена, которые есть.
Что касается инструмента SQLite, я рекомендую Браузер баз данных для SQLite, где вы можете запрашивать базу данных SQLite, выполнять запросы и даже редактировать рукой, если нужно.
person
tukan
schedule
04.11.2019
~/.local/share/evolution/addressbook/system/contacts.db
, которая представляет собой базу данных SQLite со встроенными данными vCard. Запустите указанную выше командуsqlite3
, чтобы просмотреть ее. - person bitinerant   schedule 03.11.2019