Importieren von Outlook-Kontakten in sipgate.io

Julia
23.06.2022 2 12:05 min

Was ist sipgate.io?

SMS oder Faxe senden und empfangen, den Anrufverlauf abrufen,  Anrufe initiieren und manipulieren – das alles kann sipgate.io! Mit unserer APIs können  Sie unsere Telekommunikationsfunktion flexibel in Ihre Projekte integrieren. Unsere Library und Tutorials unterstützen Sie dabei, Ihre Telefonie möglichst bequem für Sie zu gestalten. 

In diesem Tutorial

Erfahren Sie, wie Sie mithilfe  unserer REST-API die Outlook-Kontakte aus Ihrem Microsoft-Konto exportieren und in Ihr sipgate-Konto importieren können. In diesem Tutorial konzentrieren wir uns auf eine Einweg-Synchronisation, bei der die Kontakte aus Outlook in sipgate importiert und aktualisiert werden. Der umgekehrte Weg können Sie auf ähnliche Weise realisieren.

Voraussetzungen: Node.js & NPM

Aufsetzten des Projekts: sipgate.io & Contact-API

Um das Projekt aufzusetzen, verwenden Sie Node.js und die sipgate.io Node Library.

  • Erstellen Sie ein neues node.js-Projekt mit einer sipgate.js-Datei. Diese Datei wird die sipgate.io-Kontakte verwalten. 
npm init -y
touch sipgate.js
npm install sipgateio
  • Authentifizieren Sie Ihr sipgate-Konto: erstellen Sie ein Personal-Access-Token über das sipgate-Webinterface, um sich bei der REST-API authentifizieren zu können. Genauere Informationen zur Erstellung eines solchen Tokens finden Sie in unserer Documentation.

Die Anmeldedaten werden im Code mithilfe von Umgebungsvariablen übergeben und in einer .env -Datei gespeichert. Schauen Sie sich die .env.example Vorlage an. Erstellen Sie mithilfe unserer Vorlage eine .env-Datei mit allen benötigten Daten, wie z.B. SIPGATE_TOKEN_ID. 

Mithilfe von diesen Variablen erstellen Sie einen sipgate.io-Client client und ein Kontaktmodul contactsModule. 

const { sipgateIO, createContactsModule } = require("sipgateio");

const tokenId = process.env.SIPGATE_TOKEN_ID;
const token = process.env.SIPGATE_TOKEN;

if (!tokenId || !token) {
  throw Error("Please provide a valid sipgate TokenID and Token.");
}

const client = sipgateIO({ tokenId, token });
const contactsModule = createContactsModule(client);

Um die Verbindung zur API zu überprüfen, verwenden Sie die Funktion contactsModule.get: 

async function run() {
  const contacts = await contactsModule.get("SHARED");
  console.log(contacts);
}

run().catch(console.error);

Die im JSON-Format erhaltenen Kontakte sehen folgendermaßen aus:

{
  "id": "e93f8d65-7018-4a71-9815-987bae5579a8",
  "name": "Ada Lovelace",
  "emails": [
    {
      "email": "ada.lovelace@example.com",
      "type": []
    }
  ],
  "numbers": [
    {
      "number": "+97453132649",
      "type": ["cell"]
    }
  ],
  "addresses": [
    {
      "poBox": null,
      "extendedAddress": null,
      "streetAddress": "Some Address 42",
      "locality": "Berlin",
      "region": "Berlin",
      "postalCode": "10115",
      "country": "Deutschland"
    }
  ],
  "scope": "SHARED"
}

Registrieren der Integration bei Microsoft

Die Microsoft-APIs (Microsoft-Graph-API) befinden sich im Azure-Portal. Dort müssen die Anwendungen registriert werden, damit die Authentifizierung über OAuth erfolgen kann. Um diesen Schritt erfolgreich abzuschließen, folgen Sie den aufgelisteten Schritten:

    1. Melden Sie sich bei Microsoft Azure an.
    2. Gehen Sie zu Azure Active Directory.
    3. Registrieren Sie die App.
    4. Legen Sie die Redirect-URI fest, um die Verwendung im OAuth-Flow zu ermöglichen.In unserem Beispiel ist es: http://localhost:3000/auth/callback.
    5. Unter API-Permissions fügen Sie OrgContact.Read.All-Berechtigung hinzu.

In den nächsten Schritten definieren Sie weitere Umgebungsvariablen auf der Azure-Website: 

  1. AZURE_OAUTH_REDIRECT_URI: Diese Variable wird verwendet, um Sie als Teil des Azure OAuth-Prozesses zum Localhost umzuleiten. Verwenden Sie http://localhost:3000/auth/callback (siehe  Schritt 5). 
  2. AZURE_APP_SECRET: Erstellen Sie ein neues „Client Secret“ bei “Certificates and secrets“, kopieren Sie den Wert, der nach der Erstellung von dem „Client Secret“ angezeigt wird. 
  3. AZURE_APP_ID: Die Azure-Anwendungs-(Client-)ID kann direkt aus Ihrer Azure-App-Übersicht entnommen werden.
  4. AZURE_AUTHORITY: Setzen Sie die Variable auf   „https://login.microsoftonline.com/{TENANT_ID}/“. Ersetzen Sie{TENANT_ID} in der URL durch Ihre Tenant-ID, die Sie unter Eigenschaften in der Azure-Webseite finden. Dieser Link wird für die Autorisierung mit Ihrem Outlook-Konto weiterverwendet.

Microsoft Graph-API

Für die Nutzung der Microsoft-Graph-API müssen Sie ein valides Token via OAuth abrufen. Kurze Zusammenfassung zum OAuth-Ablauf:

  1. Erstellen Sie eine URL, die den Request-Scope, die Client-ID und die Redirect-URI angibt. Als Beispiel: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=…&redirect_uri=…&response_type=code&scope=…
  2. Wenn Sie die Anwendung öffnen, werden Sie über die zugewiesenen Berechtigung informiert.
  3. Melden Sie sich mit Ihrem Microsoft-Account an.
  4. Nach erfolgreicher Authentifizierung leitet Microsoft Sie zu der von Ihnen angegebenen Umleitungs-URI mit einem Query-Parameter weiter.
  5. Verwenden Sie diesen code, um das Access-Token zu erhalten. 

Für den Code-Empfang wird express verwendet:

npm install express

Starten Sie den Express-Server und folgen Sie der URL, die in 1. Schritt definiert wurde. Speichern Sie Ihr Token in token.json, um dieses später verwenden zu können. Falls das Token abgelaufen ist, wiederholen Sie die gleichen Schritte, um ein neues Token zu erzeugen. 

function authenticateOutlook(callback) {
  const app = express();
  app.get("/auth/callback", async (req, res) => {
    const code = req.query.code;

    /* ... */

    // fetch the token using the `code`
    // save the token in a local `token.json` file
    // close the server

    callback(token);
  });

  console.log(
    "Please open the URL `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=...&redirect_uri=...&response_type=code&scope=...`"
  );

  app.listen(3000);
}

Eine vollständige Implementation der oben beschriebenen Schritte finden Sie hier.

Mit diesem Token können wir nun auf die Outlook-API zugreifen, um Ihre Kontakte abzurufen.

Outlook und sipgate-Kontakte verbinden

Um Ihre Kontakte importieren zu können, laden Sie im ersten Schritt die privaten Outlook-Kontakte herunter. Um das zu erledigen, folgen Sie dieser Anleitung:

  • Verwenden Sie die Microsoft-API-Route: /me/contacts.
  • Um einen Überblick über alle Ihre Kontakte zu schaffen, erstellen Sie eine neue Klasse OutlookClient in in einer separaten Datei. Diese Klasse definiert die Client-Funktionalität für die Microsoft Graph-API.
  • Erstellen Sie einen AxiosClient, der mit dem entsprechenden Autorisierungstoken und der Microsoft Graph Base-URL konfiguriert wird.
const axios = require("axios");

const baseURL = "https://graph.microsoft.com/v1.0/";

const authority = process.env.AZURE_AUTHORITY;

class OutlookClient {
  constructor(accessToken) {
    this.token = accessToken;
    this.axios = axios.create({
      baseURL,
      headers: { Authorization: `Bearer ${this.token}` },
    });
  }
}

module.exports = {
  OutlookClient,
};

Die shared-Kontakte können mit der Route/contacts erfasst werden. Die relevanten Kontaktdaten können mit .data.value. abgerufen werden.

  async getAllOrgContacts() {
    return (await this.axios.get(`/contacts`)).data.value;
  • Erstellen Sie in der  app.js-Datei den folgenden Client, um Ihre Kontakte von Outlook abzurufen:
let outlookClient = new OutlookClient(accessToken);

let outlookContacts = await outlookClient.getAllOrgContacts();
  • Rufen Sie getAllOrgContacts() auf und bekommen Sie eine vollständige Liste Ihrer Outlook-Kontakte. 

Outlook-Kontakte in sipgate-Kontakte umwandeln

In diesem Abschnitt werden die Outlook-Kontakte durch die folgenden Schritten in sipgate-Kontakte umgewandelt.

  • Um die Outlook-Kontakte in sipgate-Kontakte umzuwandeln, implementieren Sie zwei weitere Funktionen: outlookOrgContactToSipgateContact() und outlookAddressToSipgateAddress().
  • Contact-Objekte stellen ein eigenes Mapping für Private-, Mobil- und Geschäftsnummern bereit. Analog dazu besitzen die sipgate-Kontakte eingebaute Typen, die auf die entsprechenden Nummern aus dem Outlook-Objekt gemappt werden müssen.

Verwenden Sie die OutlookNumberTypeToSipgateNumberType() Funktion, um die Outlook-Werte in die entsprechenden sipgate-Nummer-Typen TYPE_MOBILE, TYPE_HOME oder TYPE_WORK zu übersetzten.

function outlookOrgContactToSipgateContact(outlookContact) {
  const outlookNumberTypeToSipgateNumberType = (type) => {
    if (type === "home") return TYPE_HOME;
    if (type === "business") return TYPE_WORK;
    if (type === "mobile") return TYPE_MOBILE;
    return TYPE_OTHER;
  };
  const numbers = outlookOrgContact.phones
    .filter(({ number, type }) => number !== null)
    .map(({ number, type }) => ({
      number,
      type: [outlookNumberTypeToSipgateNumberType(type)],
    }));
  // ...
}
  • Analog dazu werden die E-Mail-Adressen über outlookOrgContact.mail ausgelesen und dem neuen E-Mail-Key übergeben. Die Typen für E-Mails müssen in diesem Fall nicht festgelegt werden.
function outlookOrgContactToSipgateContact(outlookOrgContact) {
  // ...
  const emails = outlookOrgContact.mail
    ? [{ email: outlookOrgContact.mail, type: [] }]
    : [];
  // ...
}
  • Definieren Sie die Werte organization und addresses. Den Namen der Organisation finden Sie als die Werte companyName und department im Outlook-Kontaktobjekt. Mappen Sie die addresses mit der Funktion outlookAddressToSipgateAddress().
function outlookOrgContactToSipgateContact(outlookOrgContact) {
  // ...
  const organization = [
    [outlookContact.companyName || "", outlookContact.department || ""],
  ];
  const addresses = outlookOrgContact.addresses.map(
    outlookAddressToSipgateAddress
  );
  // ...
}

function outlookAddressToSipgateAddress(outlookAddress) {
  return {
    streetAddress: outlookAddress.street,
    postalCode: outlookAddress.postalCode,
    locality: outlookAddress.city,
    region: outlookAddress.state,
    country: outlookAddress.countryOrRegion,
  };
}

Der Name der Kontakte name kann über outlookOrgContact.displayName ausgelesen werden. Somit wurden alle notwendigen Werte definiert, um Ihre Kontakte umzuwandeln. 

? Note: In diesem Tutorial geht es um die gemeinsamen und nicht privaten Kontakte innerhalb Ihres Unternehmens, wird der Wert scope als  SHARED übergeben.

function outlookOrgContactToSipgateContact(outlookOrgContact) {
  // ...
  return {
    name: outlookOrgContact.displayName,
    numbers,
    emails,
    organization,
    addresses,
    scope: "SHARED",
  };
}

Um doppelte oder unveränderte Kontakte zu vermeiden, verfolgen Sie Ihre Kontaktdaten mithilfe der Outlook-ID und einer UUID, die in createNewContact() erstellt wurden.

? Note: Speichern Sie diese ID-Mappings in der mapping.json-Datei.

const createNewContact = async (sipgateContact) => {
  const id = uuid.v4();
  await updateContact(id, sipgateContact);
  return id;
};

Importierte Kontakte überprüfen

Um doppelte Kontakte zu vermeiden, schaffen Sie sich einen Überblick über die bereits importierten Kontakte. Dafür mappen Sie die Outlook-Kontakte-IDs auf die sipgate-Kontakte-IDs. Dieses Mapping wird in eine JSON-Datei (mapping.json) umgewandelt, die während des Programmstarts gelesen wird.

let mapping = {};
if (await fileExists("mapping.json")) {
  const fileContents = await readFile("mapping.json");
  mapping = JSON.parse(fileContents);
}

Um dieses Mapping zu aktualisieren, schreiben Sie das aktualisierte Objekt in die mapping.json-Datei.

await writeFile("mapping.json", JSON.stringify(mapping, null, 4));

Kontakt-Import

In diesem Abschnitt werden Ihre Kontakte in Ihren sipgate-Account importiert und angepasst. 

  • Definieren Sie zwei neue Variablen, um die Anzahl der erstellten und aktualisierten Kontakte verfolgen zu können.
let nContactsUpdated = 0;
let nContactsImported = 0;
  • Jetzt beginnen Sie, Ihre Kontakte in Ihren sipgate-Account zu importieren. Damit der Prozess nicht so aufwendig wird und parallel laufen kann, wird für jeden Kontakt eine asynchrone Funktion mit der map-Methode aufgerufen.
const promises = outlookContacts.map(async (outlookContact) => {
  • Wandeln Sie die Outlook-Kontakte mit der Konvertierungsfunktion in sipgate-Kontakte um. 
    const sipgateContact = conversion.outlookOrgContactToSipgateContact(outlookContact);
      outlookContact
    );
  • Überprüfen Sie, ob der Kontakt in den bereits importierten Kontakten vorhanden ist. Falls er  bereits existiert, aktualisieren Sie den Kontakt mit der updateContact-Methode unseres sipgate-Clients. Anschließend erhöhen Sie den Zähler nContactsUpdated um eins.
if (outlookContact.id in mapping) {
  let sipgateId = mapping[outlookContact.id];
  console.log(`Contact already exists: ${outlookContact.displayName}`);
  try {
    await sipgate.updateContact(sipgateId, sipgateContact);
  } catch (error) {
    console.log(
      `failed to update contact ${sipgateContact.name}: ${error.message}`
    );
  }

  nContactsUpdated += 1;
}

Falls der Kontakt nicht in den bereits importierten Kontakten vorhanden ist, erstellen Sie einen neuen Kontakt mit createNewContact  und dem Zähler nContactsImported. Anschließend  erhöhen Sie den Zähler um eins.

  else {
      console.log(
        `Importing new Outlook contact: ${outlookContact.displayName}`
      );
      let sipgateId = await sipgate.createNewContact(sipgateContact);

      mapping[outlookContact.id] = sipgateId;
      nContactsImported += 1;
    }
  });
  • Um die Code-Ausführung anzuhalten, bis alle Promisses ausgeführt wurden, fügen Sie die Funktion all hinzu. Dieser Funktion wird die Liste unserer Promisses übergeben.
await Promise.all(promises);
  • Voilà! Ihre Outlook-Kontakte sollten nun in Ihr sipgate-Kontaktbuch importiert sein. Zum Schluss geben Sie die Anzahl der importierten und aktualisierten Kontakte aus. Um die Kontakt-Mappings zu aktualisieren, schreiben Sie das mapping-Objekt in die mappings.json-Datei.
console.log();
console.log(`${nContactsImported} contacts were imported.`);
console.log(`${nContactsUpdated} contacts already existed, updated them.`);

await writeFile("mapping.json", JSON.stringify(mapping, null, 4));

Ausführen des Scripts

Um das Skript auszuführen, legen Sie eine Datei mit dem Namen .env , die Ihre Azure- und sipgate-Anmeldedaten enthält, in dasselbe Verzeichnis wie Ihre app.js, und führen Sie es mit dem folgenden Befehl aus:

node app.js

Löschen von freigebenden Kontakten

⚠️ WARNING: Damit werden alle Ihre shared-sipgate-Kontakte gelöscht. Nur mit Vorsicht anwenden!

Wenn Sie Ihre shared-Kontakte vor dem Import aus Outlook löschen möchten, können Sie dies tun, indem Sie die mapping.json-Datei löschen und den Endpunkt /contacts der sipgate.io REST-API aufrufen.

Fügen Sie die folgende Funktion in Ihre sipgate.js-Datei ein:

const deleteAllSharedContacts = async () => {
  const sharedContacts = await contactsModule.get("SHARED");
  console.log("Deleting all shared contacts.");

  const promises = sharedContacts.map((contact) =>
    client
      .delete(`/contacts/${contact.id}`)
      .then(() =>
        console.log(`Deleting contact: ${contact.name} (${contact.id}).`)
      )
  );
  await Promise.all(promises);
};

Rufen Sie alle shared-Kontakte auf und stellen eine Delete-Anweisung für jeden von ihnen. Dafür verwenden Sie die Kontakt-ID als URL-Parameter. Auch hier wird die asynchrone Mapping-Technik für die API-Requests verwendet.

Nachdem Sie die Datei sipgate.js erweitert haben, können Sie diese in einem neuen Skript namens clean-sipgate-contacts.js verwenden.

require("dotenv").config();

const sipgate = require("./sipgate");

const fs = require("fs");
const util = require("util");

const deleteFile = util.promisify(fs.unlink);
const fileExists = util.promisify(fs.exists);

async function run() {
  if (await fileExists("./mapping.json")) {
    console.log("Deleting mapping.json.");
    await deleteFile("./mapping.json");
  }
  await sipgate.deleteAllSharedContacts();
}

run().catch(console.error);

Importieren Sie das sipgate.js-Modul: rufen Sie dessen Funktion deleteAllSharedContacts() auf und löschen Sie die mapping.json-Datei mithilfe der der promisified-Versionen der fs.unlink und fs.exists Funktionen.

Automatischer Kontakt-Import mithilfe von cronjob

Sie können den Kontaktimport automatisieren, indem Sie einen Cronjob hinzufügen. Um ihn jeden Tag, um Mitternacht auszuführen, fügen Sie die folgende Zeile in den crontab Ihres Systems ein.

0 0 * * * <user> /usr/bin/node /path/to/your/app.js

Fazit

In diesem Tutorial haben Sie gelernt, wie Sie die Outlook-Kontakte Ihres Unternehmens in Ihre sipgate-Kontakte importieren können. Dieses Code-Beispiel kann leicht erweitert werden, um private Outlook-Kontakte oder Kontakte aus anderen Quellen aufzunehmen.

Das vollständige Projekt finden Sie in unserem in unserem GitHub- Repository.

Wenn Sie mehr über die Möglichkeiten unserer sipgate.io-Library erfahren möchten, schauen Sie sich unsere anderen Tutorials in unserem Blog an. 

2 Kommentare


Oliver:

Ein Docker Image würde hier denke ich vielen Leuten helfen können.

antworten

    Boris:

    Hallo Oliver, danke für deinen Kommentar.

    Zurzeit bieten wir kein Docker Image an, nehmen diese Idee für zukünftige Projekte aber mit.

    Wir bieten alle Ressourcen für dieses Projekt in unserem Github Repository an.
    (https://github.com/sipgate-io/io-labs-outlook-contacts-import)

    Falls du weitere Fragen hast, schreib uns gerne wieder hier, auf Github oder per Mail.

    Viele Grüße,
    Boris von sipgate io

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert