Опис
Створити NodeJS демон (cron job) який через Instagram Graph API збирає дані про аудиторію користувача, кількість читачів та зберігає цю інформацію в базі даних.
Вимоги:
- Дані про аудиторію користувача та кількість читачів мають зберігатись в окрмих колекціях в MongoDB: audience_analytics (regular collection) та followers_analytics (time series collection).
- Дані про аудиторію користувача мають збиратися один раз на добу для кожного окремого Інстаграм акаунта та оновлюватися в колекції audience_analytics
- Дані про кількість читачів мають збиратися щогодини і записуватися як новий запис в followers_analytics. Новий запис створюється щогодини для кожного Інстаграм акаунта користувача.
Технічні замітки
Facebook API
Щоб збирати дані через Facebook API необхідно отримати перманентний токен з такими дозволами: pages_show_list, instagram_basic, instagram_manage_insights, pages_read_engagement, pages_manage_metadata, public_profile.
Як отримати перманентний токен
00000000000000000 - замінити на Instagram user id
followers_analytics збирається через Facebook graph api endpoint:
v14.0/00000000000000000/insights?metric=follower_count&period=day&since=1662377839 (респонс містить кількість нових читачів щодня, починаючи з дати since).
Важливо: дані надаються із запізненням на 2 дні. Приклад - сьогодні 10 вересня. Дані про нових читачів будуть за 8 і раніше. 9 та 10 вересня будуть нульові показники і оновляться у відповіді тільки 11 та 12 вересня. Нам потрібно брати дані за останні 7 днів.
v14.0/00000000000000000?fields=followers_count (загальна кількість читачів на момент запиту)
audience_analytics збирається через Facebook graph api endpoint:
v14.0/00000000000000000/insights?metric=audience_city,audience_country,audience_gender_age,audience_locale,online_followers&period=lifetime
Структура колекції в MongoDB
Схема followers_analytics time series колекції
{
"_id": ObjectId, // generated automatically by mongo
"timestamp": ISODate, //
"metadata": {
"user_id": Number, // instagram user id
},
"followers_count": Number, // total number of followers for that day.
"followers_change": [ // Array of followers change for current and previous day
{
"value": Number,
"end_time": String
},
{
"value": Number,
"end_time": String
}
]
}
followers_count - беремо з v14.0/00000000000000000?fields=followers_count API response.
followers_change - беремо з v14.0/00000000000000000/insights?metric=follower_count&period=day&since=0000000000 API response data[0].values Array.
Приклад відповіді від Фейсбук v14.0/00000000000000000/insights?metric=follower_count&period=day&since=1662377839
Facebook API response:
{
"data": [
{
"name": "follower_count",
"period": "day",
"values": [
{
"value": 5,
"end_time": "2022-09-06T07:00:00+0000"
},
{
"value": 5,
"end_time": "2022-09-07T07:00:00+0000"
},
{
"value": 5,
"end_time": "2022-09-08T07:00:00+0000"
},
{
"value": 6,
"end_time": "2022-09-09T07:00:00+0000"
},
{
"value": 4,
"end_time": "2022-09-10T07:00:00+0000"
},
{
"value": 0,
"end_time": "2022-09-11T07:00:00+0000"
},
{
"value": 0,
"end_time": "2022-09-12T07:00:00+0000"
}
],
"title": "Число подписчиков",
"description": "Общее количество уникальных аккаунтов, подписанных на этот профиль",
"id": "17841411749422633/insights/follower_count/day"
}
],
"paging": {
"previous": "https://graph.facebook.com/v14.0/17841411749422633/insights?access_token=EAAH2ZCdHE5IcBAMEXIhnA8W6Tmuo21NWY2MMMmOZBxsWvu5UpdZCDBcUettGUrZAtwiaSNRbZAvZBrBVHRLWukDgZC45QgWkZA0TcttFKgkbxLjGSW1ui2IxQdy94LPJiSylZC9dElHpAMSlt80UYe7NCrbFgjakRpJdGeyim4jViJZA4G4oCxOZAo7mmlgRZAIQNlSYlMCzqmTUZBGk7dOKi622HEtiP6ZCCK5UUZD&pretty=0&since=1661767386&metric=follower_count&period=day&until=1662377838",
"next": "https://graph.facebook.com/v14.0/17841411749422633/insights?access_token=EAAH2ZCdHE5IcBAMEXIhnA8W6Tmuo21NWY2MMMmOZBxsWvu5UpdZCDBcUettGUrZAtwiaSNRbZAvZBrBVHRLWukDgZC45QgWkZA0TcttFKgkbxLjGSW1ui2IxQdy94LPJiSylZC9dElHpAMSlt80UYe7NCrbFgjakRpJdGeyim4jViJZA4G4oCxOZAo7mmlgRZAIQNlSYlMCzqmTUZBGk7dOKi622HEtiP6ZCCK5UUZD&pretty=0&since=1662988292&metric=follower_count&period=day&until=1663598744"
}
}
Схема audience_analytics
{
"_id": ObjectId, // generated automatically by mongo
"timestamp": ISODate, //
"metadata": {
"user_id": Number, // instagram user id
},
"audience_city": Array, // Array of values from API response
"audience_country": Array, // Array of values from API response
"audience_gender_age": Array, // Array of values from API response
"audience_locale": Array, // Array of values from API response
"online_followers": Array // Array of values from API response
}
Приклад v14.0/00000000000000000/insights?metric=audience_city,audience_country,audience_gender_age,audience_locale,online_followers&period=lifetime
Facebook API response:
{
"data": [
{
"name": "audience_city",
"period": "lifetime",
"values": [
{
"value": {
"London, England": 3,
"Almere, Flevoland": 2,
"Odessa, Odessa Oblast": 7,
"Kharkiv, Kharkiv Oblast": 40,
"Barcelona, Cataluña": 4,
"Sumy, Sumy Oblast": 2,
"Amsterdam, Noord-Holland": 4,
"Kamianets-Podilskyi, Khmelnytskyi Oblast": 3,
"Alanya, Antalya Province": 2,
"Cherkasy, Cherkasy Oblast": 2,
"Lisbon, Lisbon District": 2,
"Berlin, Berlin": 18,
"Khmelnytskyi, Khmelnytskyi Oblast": 4,
"Valky, Kharkiv Oblast": 3,
"Lviv, Lviv Oblast": 23,
"Kyiv, Kyiv": 86,
"Prague, Prague": 3,
"Merefa, Kharkiv Oblast": 3,
"Moscow, Moscow": 2,
"Cologne, Nordrhein-Westfalen": 3,
"Uzhhorod, Zakarpattia Oblast": 5,
"Rivne, Rivne Oblast": 2,
"Vladivostok, Primorsky Krai": 2,
"Dnipro, Dnipropetrovsk Oblast": 6,
"Warsaw, Masovian Voivodeship": 13,
"Hoogeveen, Drenthe": 2,
"Stockholm, Stockholm County": 2,
"Dikanka, Poltava Oblast": 3,
"Chernivtsi, Chernivtsi Oblast": 6,
"Kraków, Lesser Poland Voivodeship": 2,
"Ivano-Frankivsk, Ivano-Frankivsk Oblast": 7,
"Hannoversch Münden, Niedersachsen": 2,
"Korotych, Kharkiv Oblast": 2,
"Ternopil, Ternopil Oblast": 5,
"Wroclaw, Lower Silesian Voivodeship": 8,
"Istanbul, Istanbul Province": 3,
"Toronto, Ontario": 2,
"Vinnytsia, Vinnytsia Oblast": 7,
"Frankfurt, Hessen": 2,
"Mukacheve, Zakarpattia Oblast": 2,
"Gdansk, Pomeranian Voivodeship": 2,
"Lyubotyn, Kharkiv Oblast": 2,
"Chortkiv, Ternopil Oblast": 2,
"Poltava, Poltava Oblast": 11,
"Okhtyrka, Sumy Oblast": 2
}
}
],
"title": "Audience City",
"description": "The cities of this profile's followers",
"id": "00000000000000000/insights/audience_city/lifetime"
},
{
"name": "audience_country",
"period": "lifetime",
"values": [
{
"value": {
"DE": 38,
"BE": 2,
"RU": 7,
"PT": 5,
"DK": 1,
"HR": 2,
"FR": 3,
"UA": 252,
"HU": 1,
"QA": 1,
"BR": 2,
"SE": 4,
"SK": 1,
"GB": 6,
"IE": 2,
"GE": 2,
"CA": 4,
"US": 4,
"EE": 1,
"IL": 2,
"AE": 1,
"CH": 3,
"MX": 1,
"CN": 1,
"IT": 2,
"ES": 8,
"AT": 1,
"CZ": 5,
"PH": 1,
"PK": 1,
"PL": 32,
"RO": 1,
"TR": 7,
"NL": 11,
"BA": 1
}
}
],
"title": "Audience Country",
"description": "The countries of this profile's followers",
"id": "00000000000000000/insights/audience_country/lifetime"
},
{
"name": "audience_gender_age",
"period": "lifetime",
"values": [
{
"value": {
"F.18-24": 15,
"F.25-34": 117,
"F.35-44": 15,
"F.45-54": 1,
"F.55-64": 3,
"F.65+": 2,
"M.18-24": 19,
"M.25-34": 135,
"M.35-44": 33,
"M.45-54": 2,
"M.55-64": 2,
"M.65+": 2,
"U.13-17": 1,
"U.18-24": 10,
"U.25-34": 55,
"U.35-44": 3,
"U.65+": 1
}
}
],
"title": "Gender and Age",
"description": "The gender and age distribution of this profile's followers",
"id": "00000000000000000/insights/audience_gender_age/lifetime"
},
{
"name": "audience_locale",
"period": "lifetime",
"values": [
{
"value": {
"it_IT": 1,
"es_LA": 1,
"ru_RU": 163,
"pl_PL": 7,
"da_DK": 1,
"tr_TR": 2,
"fr_FR": 3,
"de_DE": 6,
"ar_AR": 1,
"en_GB": 7,
"ru_UA": 1,
"en_US": 133,
"uk_UA": 84,
"zh_CN": 1,
"es_ES": 2,
"pt_PT": 1
}
}
],
"title": "Location",
"description": "The locales by country codes of this profile's followers",
"id": "00000000000000000/insights/audience_locale/lifetime"
},
{
"name": "online_followers",
"period": "lifetime",
"values": [
{
"value": {
},
"end_time": "2022-08-31T07:00:00+0000"
}
],
"title": "Online Followers",
"description": "Total number of this profile's followers that were online during the specified period",
"id": "00000000000000000/insights/online_followers/lifetime"
}
]
}
Архітектура

Analytics planner
Потрібно також зробити планер, який буде додавати Instagram id в чергу на оновлення аналітики користувачів, раз на добу або раз на годину в залежності від типу аналітики
Acceptance criteria:
- Використовується останній Yarn package manager.
- Використовується остання стабільна версія NodeJS.
- Raw MongoDB driver використовуєтьяс для з'єднання з MongoDB.
- Analytics daemon створюється як пакет всередені yarn workspace.
- Analytics Data (followers_analytics) зберігаєтьяс в MongoDB як time series collection https://www.mongodb.com/docs/manual/core/timeseries-collections/
- Analytics daemon збирає дані щогодини та використовує чергу щоб визначити які саме Інстаграм профілі потрібно оновити. Профілі додаються в чергу щогодини. Черга може бути поділена поміж декількох копій демона.
- Mongo Collection має використовуватися як черга (якщо можливо). Якщо це неможливо, використовувати Kafka фбо RabbitMq(краще Kafka).