diff --git a/Phone_number_migration.ipynb b/Phone_number_migration.ipynb new file mode 100644 index 0000000..71348e6 --- /dev/null +++ b/Phone_number_migration.ipynb @@ -0,0 +1,253 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "yLL1WjbsNJdP" + }, + "source": [ + "# Phone number migration\n", + "\n", + "**Перед запуском необходимо добавить файл с Oauth2 Сredentials (по умолчанию credentials.json)**\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WmCKpRemMRba" + }, + "source": [ + "Импортируем необходимые библиотеки." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "pzHzXS_eFteU" + }, + "outputs": [], + "source": [ + "import os.path\n", + "\n", + "from google.auth.transport.requests import Request\n", + "from google.oauth2.credentials import Credentials\n", + "from google_auth_oauthlib.flow import InstalledAppFlow\n", + "from googleapiclient.discovery import build\n", + "from googleapiclient.errors import HttpError" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qA-EnsZIMOeZ" + }, + "source": [ + "Основные параметры приложения." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "id": "rRgABs8rF1d8" + }, + "outputs": [], + "source": [ + "# Параметры приложения\n", + "OLD_NUMBER_PREFIXES = ['+38071', '071']\n", + "NEW_NUMBER_PREFIXES = ['+7949']\n", + "\n", + "# Функция для получения нового номера из основы старого\n", + "# Вернет номер в формате +7 949 123-45-67\n", + "def new_phone_number(base: str):\n", + " num = '7949' + base\n", + " return '+{} {} {}-{}-{}'.format(\n", + " num[0],\n", + " num[1:4],\n", + " num[4:7],\n", + " num[7:9],\n", + " num[9:11]\n", + " )\n", + "# Тип телефона.\n", + "# Доступные значения: https://developers.google.com/people/api/rest/v1/people#phonenumber\n", + "PHONE_TYPE = 'mobile'" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "id": "YMHU4Hs_F5-Y" + }, + "outputs": [], + "source": [ + "# Параметры Google API\n", + "CREDENTIALS_FILE = 'credentials.json'\n", + "TOKEN_FILE = 'token.json'\n", + "SCOPES = ['https://www.googleapis.com/auth/contacts']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-Kdstr4TOs1H" + }, + "source": [ + "Основной код приложения" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "id": "TimQdg09F2GV" + }, + "outputs": [], + "source": [ + "def main():\n", + " creds = None\n", + " if os.path.exists(TOKEN_FILE):\n", + " creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)\n", + " if not creds or not creds.valid:\n", + " if creds and creds.expired and creds.refresh_token:\n", + " creds.refresh(Request())\n", + " else:\n", + " flow = InstalledAppFlow.from_client_secrets_file(\n", + " CREDENTIALS_FILE, SCOPES)\n", + " creds = flow.run_console()\n", + " with open(TOKEN_FILE, 'w') as token:\n", + " token.write(creds.to_json())\n", + " try:\n", + " service = build('people', 'v1', credentials=creds)\n", + " print('Получаем список контактов...')\n", + " results = service.people().connections().list(resourceName='people/me',\n", + " personFields='names,phoneNumbers').execute()\n", + " connections = results.get('connections', [])\n", + " print('Количество контактов: {}'.format(len(connections)))\n", + " batch_request = {\n", + " \"contacts\": {},\n", + " \"updateMask\": \"phoneNumbers\"\n", + " }\n", + " for person in connections:\n", + " phoneNumbers = person.get('phoneNumbers', [])\n", + " has_old = False\n", + " old_numbers = set()\n", + " new_numbers = set()\n", + " # Получаем номера нового и старого формата\n", + " for phoneNumber in phoneNumbers:\n", + " num = phoneNumber.get('canonicalForm')\n", + " if not num:\n", + " continue\n", + " base = get_number_without_prefix(num, OLD_NUMBER_PREFIXES)\n", + " if (base):\n", + " has_old = True\n", + " old_numbers.add(base)\n", + " base = get_number_without_prefix(num, NEW_NUMBER_PREFIXES)\n", + " if (base):\n", + " new_numbers.add(base)\n", + " # Если нет старого - пропускаем\n", + " if not has_old:\n", + " continue\n", + " # Получаем старые номера, у которых нету парного нового\n", + " numbers_not_updated = list(\n", + " old_numbers.difference(new_numbers)\n", + " )\n", + " \n", + " # Если нет старых номеров, которые нужно обновить - пропускаем\n", + " if len(numbers_not_updated) == 0:\n", + " continue\n", + " # Получаем новые номера\n", + " new_phone_numbers = [\n", + " new_phone_number(x) for x in numbers_not_updated\n", + " ]\n", + " # batchUpdateContacts: https://developers.google.com/people/api/rest/v1/people/batchUpdateContacts\n", + " # PhoneNumber: https://developers.google.com/people/api/rest/v1/people#phonenumber\n", + " batch_request['contacts'][person['resourceName']] = {\n", + " \"phoneNumbers\": [\n", + " *person['phoneNumbers'],\n", + " [\n", + " { \n", + " \"value\": number, \n", + " \"type\": PHONE_TYPE\n", + " } for number in new_phone_numbers\n", + " ]\n", + " ],\n", + " \"etag\": person['etag']\n", + " }\n", + " contacts_len = len(batch_request['contacts'])\n", + " if (contacts_len > 0):\n", + " print('Количество контактов для обновления: {}'.format(contacts_len))\n", + " print('Обновляем контакты...')\n", + " service.people().batchUpdateContacts(body = batch_request).execute()\n", + " print('Обновление завершено!')\n", + " else:\n", + " print('Нечего обновлять!')\n", + " except HttpError as err:\n", + " print(err)\n", + "\n", + "def get_number_without_prefix(number: str, prefixes: list):\n", + " for prefix in prefixes:\n", + " if number.startswith(prefix):\n", + " return number[len(prefix):]\n", + " return False" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WnbwQVZPNDpP" + }, + "source": [ + "Запуск приложения" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 332 + }, + "id": "0v8m0ZYmGHnC", + "outputId": "092f3a86-80d3-45c7-d4d6-f72878d96017" + }, + "outputs": [ + { + "ename": "FileNotFoundError", + "evalue": "ignored", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0m__name__\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'__main__'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mmain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mmain\u001b[0;34m()\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m flow = InstalledAppFlow.from_client_secrets_file(\n\u001b[0;32m---> 10\u001b[0;31m CREDENTIALS_FILE, SCOPES)\n\u001b[0m\u001b[1;32m 11\u001b[0m \u001b[0mcreds\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mflow\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_console\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mTOKEN_FILE\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'w'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mtoken\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/google_auth_oauthlib/flow.py\u001b[0m in \u001b[0;36mfrom_client_secrets_file\u001b[0;34m(cls, client_secrets_file, scopes, **kwargs)\u001b[0m\n\u001b[1;32m 199\u001b[0m \u001b[0mFlow\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mThe\u001b[0m \u001b[0mconstructed\u001b[0m \u001b[0mFlow\u001b[0m \u001b[0minstance\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 200\u001b[0m \"\"\"\n\u001b[0;32m--> 201\u001b[0;31m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mclient_secrets_file\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"r\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mjson_file\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 202\u001b[0m \u001b[0mclient_config\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mjson_file\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 203\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'credentials.json'" + ] + } + ], + "source": [ + "if __name__ == '__main__':\n", + " main()\n" + ] + } + ], + "metadata": { + "colab": { + "name": "Phone number migration.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/main.py b/main.py index ad7ad4b..a3f8a53 100644 --- a/main.py +++ b/main.py @@ -50,7 +50,7 @@ def main(): else: flow = InstalledAppFlow.from_client_secrets_file( CREDENTIALS_FILE, SCOPES) - creds = flow.run_local_server(port=42047) + creds = flow.run_console() with open(TOKEN_FILE, 'w') as token: token.write(creds.to_json())