Как программно импортировать данные в Gnome Contacts? - PullRequest
3 голосов
/ 19 октября 2019

Я хотел бы импортировать свои контакты в Контакты Gnome через Python или командную строку. Я готов заняться программированием, но не могу найти необходимую документацию.

Gnome Contacts использует библиотеку libfolks , которая включает в себя утилиту folks-import, но нет человекастраница и вывод folks-import --help не очень полезны:

Usage:
  folks-import [OPTION…] — import meta-contact information to libfolks

Help Options:
  -h, --help            Show help options

Application Options:
  -s, --source=name     Source backend name (default: ‘pidgin’)
  --source-filename     Source filename (default: specific to source backend)

Контакты хранятся локально в ~/.local/share/evolution/addressbook/system/contacts.db, который является базой данных SQLite3, так что я мог писать прямо в этот файл. Однако я предпочел бы не без хорошей документации, особенно потому, что в базе данных, по-видимому, есть двоичные данные и данные vCard .

В Ubuntu 18.04 вы можете установить Gnome Contacts и инструменты libfolks с помощью:

sudo apt install gnome-contacts folks-tools

Вы можете проверить локальную базу данных контактов с помощью:

sqlite3 ~/.local/share/evolution/addressbook/system/contacts.db .dump

Связанные ответы, которые не решили мою проблему:

1 Ответ

0 голосов
/ 04 ноября 2019

На мой взгляд, кроме использования Evolution есть два способа импорта контактов. Для Evolution нет автоматического инструмента.

Сделайте резервную копию ваших contacts.db перед внесением любых изменений!

  1. Первый способ - это простой способ использования folks-import для импорта ваших контактов. Я проверил источник, и он, похоже, является импортером из файла blist.xml pidgin.

    • blist.xml - Локальная копия ваших списков друзей, для использования влокально применяемые псевдонимы и групповое и дружеское упорядочение между. Я попробовал это из любопытства на своем blist.xml, и он работал для некоторых контактов.

Для вас необходимо создать 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>testing@yahoo.com</email>
      <address>Berlin</address>
    </buddy>
  </contact>
  </group>
</blist>
<privacy>
</privacy>
</purple>

Когда я проверил созданный контакт, я обнаружил, что было импортировано только имя alias.

Итак, у меня есть create 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'>testing@yahoo.com</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','first_mail@gmal.com');
INSERT INTO "folder_id_email_list" VALUES ('pas-id-5DBFDF5A00000000','home_email@gmail.com');
INSERT INTO "folder_id_email_list" VALUES ('pas-id-5DBFE7F200000001','asdf@fasdf.com');
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:first_mail@gmal.com

EMAIL;TYPE=HOME:home_email@gmail.com

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:asdf@fasdf.com

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, выполнять запросы и даже редактировать вручную, если это необходимо.

...