mirror of
https://github.com/Bayselonarrend/OpenIntegrations.git
synced 2024-12-25 02:42:28 +02:00
Доработки по всем библиотекам
This commit is contained in:
parent
90d15cc392
commit
34ae49f785
20
docs/.gitignore
vendored
Normal file
20
docs/.gitignore
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Dependencies
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# Production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.docusaurus
|
||||||
|
.cache-loader
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
41
docs/README.md
Normal file
41
docs/README.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Website
|
||||||
|
|
||||||
|
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
$ yarn
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
|
||||||
|
```
|
||||||
|
$ yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
```
|
||||||
|
$ yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
This command generates static content into the `build` directory and can be served using any static contents hosting service.
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
Using SSH:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ USE_SSH=true yarn deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Not using SSH:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ GIT_USER=<Your GitHub username> yarn deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
|
3
docs/babel.config.js
Normal file
3
docs/babel.config.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||||
|
};
|
106
docs/docusaurus.config.js
Normal file
106
docs/docusaurus.config.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// @ts-check
|
||||||
|
// `@type` JSDoc annotations allow editor autocompletion and type checking
|
||||||
|
// (when paired with `@ts-check`).
|
||||||
|
// There are various equivalent ways to declare your Docusaurus config.
|
||||||
|
// See: https://docusaurus.io/docs/api/docusaurus-config
|
||||||
|
|
||||||
|
import {themes as prismThemes} from 'prism-react-renderer';
|
||||||
|
|
||||||
|
/** @type {import('@docusaurus/types').Config} */
|
||||||
|
const config = {
|
||||||
|
title: 'Открытый Пакет Интеграций',
|
||||||
|
tagline: 'Набор библиотек для интеграции с популярными API на 1C:Enterprise',
|
||||||
|
favicon: 'img/favicon.ico',
|
||||||
|
|
||||||
|
// Set the production url of your site here
|
||||||
|
url: 'https://your-docusaurus-site.example.com',
|
||||||
|
// Set the /<baseUrl>/ pathname under which your site is served
|
||||||
|
// For GitHub pages deployment, it is often '/<projectName>/'
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
|
// GitHub pages deployment config.
|
||||||
|
// If you aren't using GitHub pages, you don't need these.
|
||||||
|
organizationName: 'Bayselonarrend', // Usually your GitHub org/user name.
|
||||||
|
projectName: 'OpenIntegrations', // Usually your repo name.
|
||||||
|
|
||||||
|
onBrokenLinks: 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
|
|
||||||
|
// Even if you don't use internationalization, you can use this field to set
|
||||||
|
// useful metadata like html lang. For example, if your site is Chinese, you
|
||||||
|
// may want to replace "en" with "zh-Hans".
|
||||||
|
i18n: {
|
||||||
|
defaultLocale: 'ru',
|
||||||
|
locales: ['ru'],
|
||||||
|
},
|
||||||
|
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'classic',
|
||||||
|
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||||
|
({
|
||||||
|
theme: {
|
||||||
|
customCss: './src/css/custom.css',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
themeConfig:
|
||||||
|
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||||
|
({
|
||||||
|
// Replace with your project's social card
|
||||||
|
|
||||||
|
navbar: {
|
||||||
|
title: 'Открытый пакет интеграций',
|
||||||
|
logo: {
|
||||||
|
alt: 'OPI',
|
||||||
|
src: 'img/logo.png',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'docSidebar',
|
||||||
|
sidebarId: 'defaultSidebar',
|
||||||
|
position: 'left',
|
||||||
|
label: 'Описания методов',
|
||||||
|
},
|
||||||
|
{to: '/blog', label: 'Список изменений', position: 'left'},
|
||||||
|
{
|
||||||
|
href: 'https://github.com/Bayselonarrend/OpenIntegrations',
|
||||||
|
label: 'GitHub',
|
||||||
|
position: 'right',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
style: 'dark',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
title: 'Документация',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'Описания методов',
|
||||||
|
to: '/docs/intro',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Ресурсы',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'GitHub',
|
||||||
|
href: 'https://github.com/Bayselonarrend/OpenIntegrations',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
copyright: `${new Date().getFullYear()} OPI by Bayselonarrend`,
|
||||||
|
},
|
||||||
|
prism: {
|
||||||
|
theme: prismThemes.github,
|
||||||
|
darkTheme: prismThemes.dracula,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
14628
docs/package-lock.json
generated
Normal file
14628
docs/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
44
docs/package.json
Normal file
44
docs/package.json
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name": "my-website",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"docusaurus": "docusaurus",
|
||||||
|
"start": "docusaurus start",
|
||||||
|
"build": "docusaurus build",
|
||||||
|
"swizzle": "docusaurus swizzle",
|
||||||
|
"deploy": "docusaurus deploy",
|
||||||
|
"clear": "docusaurus clear",
|
||||||
|
"serve": "docusaurus serve",
|
||||||
|
"write-translations": "docusaurus write-translations",
|
||||||
|
"write-heading-ids": "docusaurus write-heading-ids"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@docusaurus/core": "3.0.1",
|
||||||
|
"@docusaurus/preset-classic": "3.0.1",
|
||||||
|
"@mdx-js/react": "^3.0.0",
|
||||||
|
"clsx": "^2.0.0",
|
||||||
|
"prism-react-renderer": "^2.3.0",
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@docusaurus/module-type-aliases": "3.0.1",
|
||||||
|
"@docusaurus/types": "3.0.1"
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.5%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 3 chrome version",
|
||||||
|
"last 3 firefox version",
|
||||||
|
"last 5 safari version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0"
|
||||||
|
}
|
||||||
|
}
|
33
docs/sidebars.js
Normal file
33
docs/sidebars.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Creating a sidebar enables you to:
|
||||||
|
- create an ordered group of docs
|
||||||
|
- render a sidebar for each doc of that group
|
||||||
|
- provide next/previous navigation
|
||||||
|
|
||||||
|
The sidebars can be generated from the filesystem, or explicitly defined here.
|
||||||
|
|
||||||
|
Create as many sidebars as you want.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||||
|
const sidebars = {
|
||||||
|
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||||
|
tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
|
||||||
|
|
||||||
|
// But you can create a sidebar manually
|
||||||
|
/*
|
||||||
|
tutorialSidebar: [
|
||||||
|
'intro',
|
||||||
|
'hello',
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Tutorial',
|
||||||
|
items: ['tutorial-basics/create-a-document'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
export default sidebars;
|
@ -1,11 +0,0 @@
|
|||||||
Функция СоздатьJWT(Знач СлужебныйКлюч, Знач Почта) Экспорт
|
|
||||||
|
|
||||||
Заголовок = "{""alg"":""RS256"",""typ"":""JWT""}";
|
|
||||||
Области = "https://www.googleapis.com/auth/drive, "
|
|
||||||
+ "https://www.googleapis.com/auth/calendar, "
|
|
||||||
+ "https://www.googleapis.com/auth/spreadsheets ";
|
|
||||||
|
|
||||||
Данные = Новый Соответствие;
|
|
||||||
Данные.Вставить("iss", Почта);
|
|
||||||
|
|
||||||
КонецФункции
|
|
@ -20,6 +20,8 @@
|
|||||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
//SOFTWARE.
|
//SOFTWARE.
|
||||||
|
|
||||||
|
//https://github.com/Bayselonarrend/OpenIntegrations
|
||||||
|
|
||||||
#Область ПрограммныйИнтерфейс
|
#Область ПрограммныйИнтерфейс
|
||||||
|
|
||||||
#Область Бот
|
#Область Бот
|
||||||
@ -325,7 +327,7 @@
|
|||||||
|
|
||||||
Параметры_.Вставить("media", OPI_Инструменты.JSONСтрокой(Медиа));
|
Параметры_.Вставить("media", OPI_Инструменты.JSONСтрокой(Медиа));
|
||||||
|
|
||||||
Ответ = OPI_Инструменты.Post("api.telegram.org/bot"
|
Ответ = OPI_Инструменты.PostMultipart("api.telegram.org/bot"
|
||||||
+ Токен
|
+ Токен
|
||||||
+ "/sendMediaGroup", Параметры_, СтруктураФайлов, "mixed");
|
+ "/sendMediaGroup", Параметры_, СтруктураФайлов, "mixed");
|
||||||
|
|
||||||
@ -634,7 +636,7 @@
|
|||||||
Ключ = "WebAppData";
|
Ключ = "WebAppData";
|
||||||
Хэш = "";
|
Хэш = "";
|
||||||
|
|
||||||
Результат = OPI_Инструменты.HMACSHA256(ПолучитьДвоичныеДанныеИзСтроки(Ключ)
|
Результат = OPI_Криптография.HMACSHA256(ПолучитьДвоичныеДанныеИзСтроки(Ключ)
|
||||||
, ПолучитьДвоичныеДанныеИзСтроки(Токен));
|
, ПолучитьДвоичныеДанныеИзСтроки(Токен));
|
||||||
|
|
||||||
ТЗнач = Новый ТаблицаЗначений;
|
ТЗнач = Новый ТаблицаЗначений;
|
||||||
@ -666,7 +668,7 @@
|
|||||||
КонецЦикла;
|
КонецЦикла;
|
||||||
|
|
||||||
DCS = Лев(DCS, СтрДлина(DCS) - 1);
|
DCS = Лев(DCS, СтрДлина(DCS) - 1);
|
||||||
Подпись = OPI_Инструменты.HMACSHA256(Результат, ПолучитьДвоичныеДанныеИзСтроки(DCS));
|
Подпись = OPI_Криптография.HMACSHA256(Результат, ПолучитьДвоичныеДанныеИзСтроки(DCS));
|
||||||
|
|
||||||
Финал = ПолучитьHexСтрокуИзДвоичныхДанных(Подпись);
|
Финал = ПолучитьHexСтрокуИзДвоичныхДанных(Подпись);
|
||||||
|
|
||||||
@ -768,7 +770,7 @@
|
|||||||
СтруктураФайлов = Новый Структура;
|
СтруктураФайлов = Новый Структура;
|
||||||
СтруктураФайлов.Вставить(Вид + Расширение, Файл);
|
СтруктураФайлов.Вставить(Вид + Расширение, Файл);
|
||||||
|
|
||||||
Ответ = OPI_Инструменты.Post("api.telegram.org/bot"
|
Ответ = OPI_Инструменты.PostMultipart("api.telegram.org/bot"
|
||||||
+ Токен
|
+ Токен
|
||||||
+ Метод, Параметры_, СтруктураФайлов, "mixed");
|
+ Метод, Параметры_, СтруктураФайлов, "mixed");
|
||||||
|
|
||||||
|
343
src/CommonModules/OPI_Twitter/Module.bsl
Normal file
343
src/CommonModules/OPI_Twitter/Module.bsl
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
//MIT License
|
||||||
|
|
||||||
|
//Copyright (c) 2023 Anton Tsitavets
|
||||||
|
|
||||||
|
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
//of this software and associated documentation files (the "Software"), to deal
|
||||||
|
//in the Software without restriction, including without limitation the rights
|
||||||
|
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
//copies of the Software, and to permit persons to whom the Software is
|
||||||
|
//furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
//The above copyright notice and this permission notice shall be included in all
|
||||||
|
//copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
//SOFTWARE.
|
||||||
|
|
||||||
|
//https://github.com/Bayselonarrend/OpenIntegrations
|
||||||
|
//Если в не знаете с чего начать, то стоит найти метод ПолучитьСтандартныеПараметры()
|
||||||
|
//и почитать комментарии
|
||||||
|
|
||||||
|
#Область ПрограммныйИнтерфейс
|
||||||
|
|
||||||
|
#Область ДанныеИНастройка
|
||||||
|
|
||||||
|
// Получить ссылку для авторизации через браузер.
|
||||||
|
//
|
||||||
|
// Параметры:
|
||||||
|
// Параметры - Соответствие Из Строка - См.ПолучитьСтандартныеПараметры
|
||||||
|
//
|
||||||
|
// Возвращаемое значение:
|
||||||
|
// Строка - URL для перехода в браузере
|
||||||
|
Функция ПолучитьСсылкуАвторизации(Параметры = "") Экспорт
|
||||||
|
|
||||||
|
Параметры_ = ПолучитьСтандартныеПараметры(Параметры);
|
||||||
|
|
||||||
|
ПараметрыURL = Новый Структура;
|
||||||
|
|
||||||
|
ПараметрыURL.Вставить("response_type" , "code");
|
||||||
|
ПараметрыURL.Вставить("client_id" , Параметры_["client_id"]);
|
||||||
|
ПараметрыURL.Вставить("redirect_uri" , Параметры_["redirect_uri"]);
|
||||||
|
ПараметрыURL.Вставить("scope" , Параметры_["scope"]);
|
||||||
|
ПараметрыURL.Вставить("state" , "state");
|
||||||
|
ПараметрыURL.Вставить("code_challenge" , "challenge");
|
||||||
|
ПараметрыURL.Вставить("code_challenge_method", "plain");
|
||||||
|
|
||||||
|
Линк = "https://twitter.com/i/oauth2/authorize"
|
||||||
|
+ OPI_Инструменты.ПараметрыЗапросаВСтроку(ПараметрыURL);
|
||||||
|
|
||||||
|
Возврат Линк;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
// Получить токен.
|
||||||
|
//
|
||||||
|
// Параметры:
|
||||||
|
// Код - Строка - Код, полученный из авторизации См.ПолучитьСсылкуАвторизации
|
||||||
|
// Параметры - Соответствие Из Строка - См.ПолучитьСтандартныеПараметры
|
||||||
|
//
|
||||||
|
// Возвращаемое значение:
|
||||||
|
// HTTPОтвет, Произвольный, ДвоичныеДанные - Результат чтения JSON ответа сервера
|
||||||
|
Функция ПолучитьТокен(Знач Код, Знач Параметры = "") Экспорт
|
||||||
|
|
||||||
|
Параметры_ = ПолучитьСтандартныеПараметры(Параметры);
|
||||||
|
|
||||||
|
ПараметрыЗапроса = Новый Структура;
|
||||||
|
ПараметрыЗапроса.Вставить("code" , Код);
|
||||||
|
ПараметрыЗапроса.Вставить("grant_type" , "authorization_code");
|
||||||
|
ПараметрыЗапроса.Вставить("client_id" , Параметры_["client_id"]);
|
||||||
|
ПараметрыЗапроса.Вставить("redirect_uri" , Параметры_["redirect_uri"]);
|
||||||
|
ПараметрыЗапроса.Вставить("code_verifier", "challenge");
|
||||||
|
|
||||||
|
Ответ = OPI_Инструменты.Post("https://api.twitter.com/2/oauth2/token"
|
||||||
|
, ПараметрыЗапроса, , Ложь);
|
||||||
|
|
||||||
|
Возврат Ответ;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
// Обновить токен v2 токен при помощи refresh_token
|
||||||
|
//
|
||||||
|
// Параметры:
|
||||||
|
// Параметры - Соответствие Из Строка - См.ПолучитьСтандартныеПараметры
|
||||||
|
//
|
||||||
|
// Возвращаемое значение:
|
||||||
|
// HTTPОтвет, Произвольный, ДвоичныеДанные - Результат чтения JSON ответа сервера
|
||||||
|
Функция ОбновитьТокен(Знач Параметры = "") Экспорт
|
||||||
|
|
||||||
|
Параметры_ = ПолучитьСтандартныеПараметры(Параметры);
|
||||||
|
|
||||||
|
ПараметрыЗапроса = Новый Структура;
|
||||||
|
ПараметрыЗапроса.Вставить("refresh_token", Параметры_["refresh_token"]);
|
||||||
|
ПараметрыЗапроса.Вставить("grant_type" , "refresh_token");
|
||||||
|
ПараметрыЗапроса.Вставить("client_id" , Параметры_["client_id"]);
|
||||||
|
|
||||||
|
Ответ = OPI_Инструменты.Post("https://api.twitter.com/2/oauth2/token"
|
||||||
|
, ПараметрыЗапроса, , Ложь);
|
||||||
|
|
||||||
|
Возврат Ответ;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
// Метод для вставки в http-сервис, адрес которого указывается в redirect_uri
|
||||||
|
// Вызывает метод получения токена, так как для получения токена из кода, приходящего
|
||||||
|
// на redirect_uri после авторизации через браузер есть всего 30 секунд
|
||||||
|
//
|
||||||
|
// Параметры:
|
||||||
|
// Запрос - HTTPСервисЗапрос - Запрос, приходящий на http-сервис
|
||||||
|
//
|
||||||
|
// Возвращаемое значение:
|
||||||
|
// HTTPОтвет, Произвольный, ДвоичныеДанные - Результат чтения JSON ответа сервера
|
||||||
|
Функция ОбработкаВходящегоЗапросаПослеАвторизации(Запрос) Экспорт
|
||||||
|
|
||||||
|
Код = Запрос.ПараметрыЗапроса["code"];
|
||||||
|
ОтветТокен = ПолучитьТокен(Код);
|
||||||
|
|
||||||
|
//Предпочтительное хранение токенов
|
||||||
|
//Константы.TwitterRefresh.Установить(ОтветТокен["refresh_token"]);
|
||||||
|
//Константы.TwitterToken.Установить(ОтветТокен["access_token"]);
|
||||||
|
|
||||||
|
Возврат ОтветТокен;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
#КонецОбласти
|
||||||
|
|
||||||
|
#Область Твиты
|
||||||
|
|
||||||
|
// Создать твит.
|
||||||
|
//
|
||||||
|
// Параметры:
|
||||||
|
// Текст - Строка - Текст твита
|
||||||
|
// Параметры - Соответствие Из Строка - См.ПолучитьСтандартныеПараметры
|
||||||
|
//
|
||||||
|
// Возвращаемое значение:
|
||||||
|
// HTTPОтвет, Произвольный, ДвоичныеДанные - Результат чтения JSON ответа сервера
|
||||||
|
Функция СоздатьТвит(Знач Текст, Знач Параметры) Экспорт
|
||||||
|
|
||||||
|
Параметры_ = ПолучитьСтандартныеПараметры(Параметры);
|
||||||
|
Авторизация = СоздатьЗаголовокАвторизацииV2(Параметры_);
|
||||||
|
URL = "https://api.twitter.com/2/tweets";
|
||||||
|
|
||||||
|
Поля = Новый Соответствие;
|
||||||
|
Поля.Вставить("text", Текст);
|
||||||
|
|
||||||
|
Ответ = OPI_Инструменты.Post(URL, Поля, Авторизация);
|
||||||
|
|
||||||
|
Возврат Ответ;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
#КонецОбласти
|
||||||
|
|
||||||
|
#КонецОбласти
|
||||||
|
|
||||||
|
#Область СлужебныеПроцедурыИФункции
|
||||||
|
|
||||||
|
Функция ПолучитьСтандартныеПараметры(Знач Параметры = "")
|
||||||
|
|
||||||
|
//Здесь собрано определение данных, необходимых для работы.
|
||||||
|
//Для Twitter это довольно значительный набор, что обсуловлено наличием сразу 2-х API,
|
||||||
|
//которые, при этом, созданы не для разныз задач, но просто являются версиями друг друга.
|
||||||
|
//Актуальной версией API является v2 и она требует получения временных токенов. Несмотря на то,
|
||||||
|
//что Twitter настаивает на использовании этой актуальной версии, они как-то умудрились не перенести
|
||||||
|
//механизм загрузки файлов и некоторые другие из старой версии - v1.1. По-этому что-то нужно делать
|
||||||
|
//на версии 1.1, а что-то на 2: вплоть до того что они убрали возможность постить твиты из v1.1,
|
||||||
|
//но только через нее в твит можно добавить картинку. При этом способы авторизации и токены у них разные
|
||||||
|
|
||||||
|
//Мировая гигокорпорация Илона Маска, кстати, напоминаю ;)
|
||||||
|
|
||||||
|
//P.S Далее часто упоминается "страница настроек Twitter Developer" - это
|
||||||
|
//https://developer.twitter.com/en/portal/dashboard и выбор конкретного проекта из списка (значек c ключем)
|
||||||
|
|
||||||
|
Параметры_ = Новый Соответствие;
|
||||||
|
|
||||||
|
Разрешения = "tweet.read tweet.write tweet.moderate.write users.read "
|
||||||
|
+ "follows.read follows.write offline.access space.read mute.read "
|
||||||
|
+ "mute.write like.read like.write list.read list.write block.read "
|
||||||
|
+ "block.write bookmark.read bookmark.write";
|
||||||
|
|
||||||
|
|
||||||
|
//Данные для API v2
|
||||||
|
|
||||||
|
//redirect_uri - URL вашего http-сервиса (или другого обработчика запросов) для авторизации
|
||||||
|
//scope - набор разрешений для получаемого ключа. Может быть любой, но offline.access обязателен
|
||||||
|
//client_id - Из OAuth 2.0 Client ID and Client Secret страницы настроек Twitter Developer
|
||||||
|
//client_secret - Из OAuth 2.0 Client ID and Client Secret страницы настроек Twitter Developer
|
||||||
|
//access_token - ПолучитьСсылкуАвторизации() -> Браузер -> http-запрос с code придет на адрес redirect_uri -> ПолучитьТокен(code)
|
||||||
|
//refresh_token - Приходит вместе с access_token и используется для его обновления (время жизни access_token - 2 часа).
|
||||||
|
// Обновление происходит методом ОбновитьТокен с новыми access_token и refresh_token. При следующем обновлении
|
||||||
|
// нужно использовать уже новый refresh_token, так что захардкодить не получится (access_token тоже не получится)
|
||||||
|
|
||||||
|
// |--> ОбновитьТокен() ->|access_token --> Используется в т-нии 2-х часов для запросов
|
||||||
|
// | |refresh_token --|
|
||||||
|
// |--------[через 2 ч.]-------------------|
|
||||||
|
|
||||||
|
|
||||||
|
//Данные для API v1.1
|
||||||
|
|
||||||
|
//oauth_token - из Authentication Tokens -> Access Token and Secret страницы настроек Twitter Developer
|
||||||
|
//oauth_token_secret - из Authentication Tokens -> Access Token and Secret страницы настроек Twitter Developer
|
||||||
|
//oauth_consumer_key - из Consumer Keys -> Access Token and Secret страницы настроек Twitter Developer
|
||||||
|
//oauth_consumer_secret - из Consumer Keys -> Access Token and Secret страницы настроек Twitter Developer
|
||||||
|
|
||||||
|
//Эти токены обновлять не надо
|
||||||
|
|
||||||
|
Параметры_.Вставить("redirect_uri" , "");
|
||||||
|
Параметры_.Вставить("scope" , Разрешения);
|
||||||
|
Параметры_.Вставить("client_id" , "");
|
||||||
|
Параметры_.Вставить("client_secret" , "");
|
||||||
|
Параметры_.Вставить("access_token" , ""); //Должно быть нечто вроде Константы.TwitterToken.Получить()
|
||||||
|
Параметры_.Вставить("refresh_token" , ""); //Должно быть нечто вроде Константы.TwitterRefresh.Получить()
|
||||||
|
Параметры_.Вставить("oauth_token" , "");
|
||||||
|
Параметры_.Вставить("oauth_token_secret" , "");
|
||||||
|
Параметры_.Вставить("oauth_consumer_key" , "");
|
||||||
|
Параметры_.Вставить("oauth_consumer_secret", "");
|
||||||
|
|
||||||
|
Если ТипЗнч(Параметры) = Тип("Структура") Или ТипЗнч(Параметры) = Тип("Соответствие") Тогда
|
||||||
|
Для Каждого ПереданныйПараметр Из Параметры Цикл
|
||||||
|
Параметры_.Вставить(ПереданныйПараметр.Ключ, OPI_Инструменты.ЧислоВСтроку(ПереданныйПараметр.Значение));
|
||||||
|
КонецЦикла;
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
Возврат Параметры_;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
Функция СоздатьЗаголовокАвторизацииV1(Знач Параметры, Знач Поля, Знач ВидЗапроса, Знач URL)
|
||||||
|
|
||||||
|
ЗаголовокАвторизации = "";
|
||||||
|
МетодХэширования = "HMAC-SHA1";
|
||||||
|
ВерсияАпи = "1.0";
|
||||||
|
СтрокаСигнатуры = "";
|
||||||
|
Подпись = "";
|
||||||
|
ТекущаяДатаUNIX = OPI_Инструменты.UNIXTime(ТекущаяДатаСеанса());
|
||||||
|
ТекущаяДатаUNIX = OPI_Инструменты.ЧислоВСтроку(ТекущаяДатаUNIX);
|
||||||
|
ТаблицаПараметров = Новый ТаблицаЗначений;
|
||||||
|
ТаблицаПараметров.Колонки.Добавить("Ключ");
|
||||||
|
ТаблицаПараметров.Колонки.Добавить("Значение");
|
||||||
|
|
||||||
|
|
||||||
|
Для Каждого Поле Из Поля Цикл
|
||||||
|
|
||||||
|
НоваяСтрока = ТаблицаПараметров.Добавить();
|
||||||
|
НоваяСтрока.Ключ = Поле.Ключ;
|
||||||
|
НоваяСтрока.Значение = Поле.Значение;
|
||||||
|
|
||||||
|
КонецЦикла;
|
||||||
|
|
||||||
|
НоваяСтрока = ТаблицаПараметров.Добавить();
|
||||||
|
НоваяСтрока.Ключ = "oauth_consumer_key";
|
||||||
|
НоваяСтрока.Значение = Параметры["oauth_consumer_key"];
|
||||||
|
|
||||||
|
НоваяСтрока = ТаблицаПараметров.Добавить();
|
||||||
|
НоваяСтрока.Ключ = "oauth_token";
|
||||||
|
НоваяСтрока.Значение = Параметры["oauth_token"];
|
||||||
|
|
||||||
|
НоваяСтрока = ТаблицаПараметров.Добавить();
|
||||||
|
НоваяСтрока.Ключ = "oauth_version";
|
||||||
|
НоваяСтрока.Значение = ВерсияАпи;
|
||||||
|
|
||||||
|
НоваяСтрока = ТаблицаПараметров.Добавить();
|
||||||
|
НоваяСтрока.Ключ = "oauth_signature_method";
|
||||||
|
НоваяСтрока.Значение = МетодХэширования;
|
||||||
|
|
||||||
|
НоваяСтрока = ТаблицаПараметров.Добавить();
|
||||||
|
НоваяСтрока.Ключ = "oauth_timestamp";
|
||||||
|
НоваяСтрока.Значение = ТекущаяДатаUNIX;
|
||||||
|
|
||||||
|
НоваяСтрока = ТаблицаПараметров.Добавить();
|
||||||
|
НоваяСтрока.Ключ = "oauth_nonce";
|
||||||
|
НоваяСтрока.Значение = ТекущаяДатаUNIX;
|
||||||
|
|
||||||
|
Для Каждого СтрокаТаблицы Из ТаблицаПараметров Цикл
|
||||||
|
|
||||||
|
СтрокаТаблицы.Ключ = КодироватьСтроку(СтрокаТаблицы.Ключ, СпособКодированияСтроки.КодировкаURL);
|
||||||
|
СтрокаТаблицы.Значение = КодироватьСтроку(СтрокаТаблицы.Значение, СпособКодированияСтроки.КодировкаURL);
|
||||||
|
|
||||||
|
КонецЦикла;
|
||||||
|
|
||||||
|
ТаблицаПараметров.Сортировать("Ключ");
|
||||||
|
|
||||||
|
Для Каждого СтрокаТаблицы Из ТаблицаПараметров Цикл
|
||||||
|
|
||||||
|
СтрокаСигнатуры = СтрокаСигнатуры
|
||||||
|
+ СтрокаТаблицы.Ключ
|
||||||
|
+ "="
|
||||||
|
+ СтрокаТаблицы.Значение
|
||||||
|
+ "&";
|
||||||
|
|
||||||
|
КонецЦикла;
|
||||||
|
|
||||||
|
СтрокаСигнатуры = Лев(СтрокаСигнатуры, СтрДлина(СтрокаСигнатуры) - 1);
|
||||||
|
СтрокаСигнатуры = вРег(ВидЗапроса)
|
||||||
|
+ "&"
|
||||||
|
+ КодироватьСтроку(URL, СпособКодированияСтроки.КодировкаURL)
|
||||||
|
+ "&"
|
||||||
|
+ КодироватьСтроку(СтрокаСигнатуры, СпособКодированияСтроки.КодировкаURL);
|
||||||
|
|
||||||
|
Подпись = КодироватьСтроку(Параметры["oauth_consumer_secret"], СпособКодированияСтроки.КодировкаURL)
|
||||||
|
+ "&"
|
||||||
|
+ КодироватьСтроку(Параметры["oauth_token_secret"], СпособКодированияСтроки.КодировкаURL);
|
||||||
|
|
||||||
|
|
||||||
|
Сигнатура = OPI_Криптография.HMAC(ПолучитьДвоичныеДанныеИзСтроки(Подпись)
|
||||||
|
, ПолучитьДвоичныеДанныеИзСтроки(СтрокаСигнатуры)
|
||||||
|
, ХешФункция.SHA1
|
||||||
|
, 64);
|
||||||
|
|
||||||
|
Сигнатура = КодироватьСтроку(Base64Строка(Сигнатура), СпособКодированияСтроки.КодировкаURL);
|
||||||
|
|
||||||
|
ЗаголовокАвторизации = ЗаголовокАвторизации
|
||||||
|
+ "OAuth "
|
||||||
|
+ "oauth_consumer_key=""" + Параметры["oauth_consumer_key"] + ""","
|
||||||
|
+ "oauth_token=""" + Параметры["oauth_token"] + ""","
|
||||||
|
+ "oauth_signature_method=""" + МетодХэширования + ""","
|
||||||
|
+ "oauth_timestamp=""" + ТекущаяДатаUNIX + ""","
|
||||||
|
+ "oauth_nonce=""" + ТекущаяДатаUNIX + ""","
|
||||||
|
+ "oauth_version=""" + ВерсияАпи + ""","
|
||||||
|
+ "oauth_signature=""" + Сигнатура;
|
||||||
|
|
||||||
|
СоответствиеЗаголовка = Новый Соответствие;
|
||||||
|
СоответствиеЗаголовка.Вставить("authorization", ЗаголовокАвторизации);
|
||||||
|
|
||||||
|
Возврат СоответствиеЗаголовка;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
Функция СоздатьЗаголовокАвторизацииV2(Знач Параметры)
|
||||||
|
|
||||||
|
СоответствиеВозврата = Новый Соответствие;
|
||||||
|
СоответствиеВозврата.Вставить("Authorization", "Bearer " + Параметры["access_token"]);
|
||||||
|
|
||||||
|
Возврат СоответствиеВозврата;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
#КонецОбласти
|
@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<mdclass:CommonModule xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass" uuid="cf56b647-8ad6-4ee3-a5ae-8dcf339b1647">
|
<mdclass:CommonModule xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass" uuid="31cd7c60-0873-4333-99c1-fc4807082ea9">
|
||||||
<name>OPI_GoogleWorkspace</name>
|
<name>OPI_Twitter</name>
|
||||||
<synonym>
|
<synonym>
|
||||||
<key>ru</key>
|
<key>ru</key>
|
||||||
<value>Методы Google Workspace</value>
|
<value>OPI twitter</value>
|
||||||
</synonym>
|
</synonym>
|
||||||
<server>true</server>
|
<server>true</server>
|
||||||
<externalConnection>true</externalConnection>
|
<externalConnection>true</externalConnection>
|
@ -20,6 +20,10 @@
|
|||||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
//SOFTWARE.
|
//SOFTWARE.
|
||||||
|
|
||||||
|
//https://github.com/Bayselonarrend/OpenIntegrations
|
||||||
|
//Если в не знаете с чего начать, то стоит найти метод ПолучитьСтандартныеПараметры()
|
||||||
|
//и почитать комментарии
|
||||||
|
|
||||||
#Область ПрограммныйИнтерфейс
|
#Область ПрограммныйИнтерфейс
|
||||||
|
|
||||||
#Область ПолучениеТокена
|
#Область ПолучениеТокена
|
||||||
@ -86,7 +90,7 @@
|
|||||||
|
|
||||||
Параметры_.Вставить("upload_url", URL);
|
Параметры_.Вставить("upload_url", URL);
|
||||||
|
|
||||||
Ответ = OPI_Инструменты.Post(URL, Параметры_, Файлы);
|
Ответ = OPI_Инструменты.PostMultipart(URL, Параметры_, Файлы);
|
||||||
СерверФото = OPI_Инструменты.ЧислоВСтроку(Ответ["server"]);
|
СерверФото = OPI_Инструменты.ЧислоВСтроку(Ответ["server"]);
|
||||||
|
|
||||||
Параметры_.Вставить("hash" , Ответ["hash"]);
|
Параметры_.Вставить("hash" , Ответ["hash"]);
|
||||||
@ -173,7 +177,7 @@
|
|||||||
Файлы = Новый Соответствие;
|
Файлы = Новый Соответствие;
|
||||||
Файлы.Вставить(Путь, Картинка);
|
Файлы.Вставить(Путь, Картинка);
|
||||||
|
|
||||||
Ответ = OPI_Инструменты.Post(URL, Параметры_, Файлы);
|
Ответ = OPI_Инструменты.PostMultipart(URL, Параметры_, Файлы);
|
||||||
|
|
||||||
Параметры_.Вставить("hash" , Ответ["hash"]);
|
Параметры_.Вставить("hash" , Ответ["hash"]);
|
||||||
Параметры_.Вставить("photo" , Ответ["photo"]);
|
Параметры_.Вставить("photo" , Ответ["photo"]);
|
||||||
@ -280,7 +284,7 @@
|
|||||||
Файлы = Новый Соответствие;
|
Файлы = Новый Соответствие;
|
||||||
Файлы.Вставить(Путь, Картинка);
|
Файлы.Вставить(Путь, Картинка);
|
||||||
|
|
||||||
Ответ = OPI_Инструменты.Post(URL, Параметры_, Файлы);
|
Ответ = OPI_Инструменты.PostMultipart(URL, Параметры_, Файлы);
|
||||||
Параметры_.Вставить("upload_results", Ответ["response"]["upload_result"]);
|
Параметры_.Вставить("upload_results", Ответ["response"]["upload_result"]);
|
||||||
|
|
||||||
Ответ = OPI_Инструменты.Get("api.vk.com/method/stories.save", Параметры_);
|
Ответ = OPI_Инструменты.Get("api.vk.com/method/stories.save", Параметры_);
|
||||||
@ -321,7 +325,7 @@
|
|||||||
Файлы = Новый Соответствие;
|
Файлы = Новый Соответствие;
|
||||||
Файлы.Вставить(Путь, Картинка);
|
Файлы.Вставить(Путь, Картинка);
|
||||||
|
|
||||||
ОтветАльбома = OPI_Инструменты.Post(URL, Параметры_, Файлы);
|
ОтветАльбома = OPI_Инструменты.PostMultipart(URL, Параметры_, Файлы);
|
||||||
|
|
||||||
Параметры_.Вставить("server" , OPI_Инструменты.ЧислоВСтроку(ОтветАльбома["server"]));
|
Параметры_.Вставить("server" , OPI_Инструменты.ЧислоВСтроку(ОтветАльбома["server"]));
|
||||||
Параметры_.Вставить("photos_list" , ОтветАльбома["photos_list"]);
|
Параметры_.Вставить("photos_list" , ОтветАльбома["photos_list"]);
|
||||||
@ -880,12 +884,23 @@
|
|||||||
|
|
||||||
Функция ПолучитьСтандартныеПараметры(Знач Параметры = "")
|
Функция ПолучитьСтандартныеПараметры(Знач Параметры = "")
|
||||||
|
|
||||||
|
//Здесь собрано определение данных для работы с VK API
|
||||||
|
//Вы можете переопределять их, передавая в качестве параметра
|
||||||
|
//Совпадающие поля будут перезаписаны с приоритетом параметра функции
|
||||||
|
|
||||||
Параметры_ = Новый Структура;
|
Параметры_ = Новый Структура;
|
||||||
|
|
||||||
|
//access_token - можно получить в браузере по URL из функции СоздатьСсылкуПолученияТокена()
|
||||||
|
//from_group - действия будут выполняться от лица группы
|
||||||
|
//owner_id - id группы с "-" в начале. Можно найти в настройках группы ВК или в ее URL, если не был
|
||||||
|
// установлен свой
|
||||||
|
//app_id - id приложения, которое необходимо создать в профиле на странице для разработчиков
|
||||||
|
//group_id - owner_id, но без "-"
|
||||||
|
|
||||||
Параметры_.Вставить("access_token" , "");
|
Параметры_.Вставить("access_token" , "");
|
||||||
Параметры_.Вставить("from_group" , "");
|
Параметры_.Вставить("from_group" , "1");
|
||||||
Параметры_.Вставить("owner_id" , "");
|
Параметры_.Вставить("owner_id" , "");
|
||||||
Параметры_.Вставить("v" , "");
|
Параметры_.Вставить("v" , "5.131");
|
||||||
Параметры_.Вставить("app_id" , "");
|
Параметры_.Вставить("app_id" , "");
|
||||||
Параметры_.Вставить("group_id" , "");
|
Параметры_.Вставить("group_id" , "");
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
//SOFTWARE.
|
//SOFTWARE.
|
||||||
|
|
||||||
|
//https://github.com/Bayselonarrend/OpenIntegrations
|
||||||
|
|
||||||
#Область ПрограммныйИнтерфейс
|
#Область ПрограммныйИнтерфейс
|
||||||
|
|
||||||
#Область НастройкиИИнформация
|
#Область НастройкиИИнформация
|
||||||
@ -41,7 +43,7 @@
|
|||||||
СтруктураПараметров.Вставить("url" , URL);
|
СтруктураПараметров.Вставить("url" , URL);
|
||||||
СтруктураПараметров.Вставить("auth_token", Токен);
|
СтруктураПараметров.Вставить("auth_token", Токен);
|
||||||
|
|
||||||
Возврат OPI_Инструменты.PostUrlencoded("https://chatapi.viber.com/pa/set_webhook"
|
Возврат OPI_Инструменты.Post("https://chatapi.viber.com/pa/set_webhook"
|
||||||
, СтруктураПараметров);
|
, СтруктураПараметров);
|
||||||
|
|
||||||
КонецФункции
|
КонецФункции
|
||||||
@ -75,7 +77,7 @@
|
|||||||
СтруктураПараметров = Новый Структура;
|
СтруктураПараметров = Новый Структура;
|
||||||
СтруктураПараметров.Вставить("id", IDПользователя);
|
СтруктураПараметров.Вставить("id", IDПользователя);
|
||||||
|
|
||||||
Ответ = OPI_Инструменты.PostUrlencoded("https://chatapi.viber.com/pa/get_user_details"
|
Ответ = OPI_Инструменты.Post("https://chatapi.viber.com/pa/get_user_details"
|
||||||
, СтруктураПараметров
|
, СтруктураПараметров
|
||||||
, ТокенВЗаголовки(Токен));
|
, ТокенВЗаголовки(Токен));
|
||||||
|
|
||||||
@ -108,7 +110,7 @@
|
|||||||
СтруктураПараметров = Новый Структура;
|
СтруктураПараметров = Новый Структура;
|
||||||
СтруктураПараметров.Вставить("ids", IDПользователей);
|
СтруктураПараметров.Вставить("ids", IDПользователей);
|
||||||
|
|
||||||
Ответ = OPI_Инструменты.PostUrlencoded("https://chatapi.viber.com/pa/get_online"
|
Ответ = OPI_Инструменты.Post("https://chatapi.viber.com/pa/get_online"
|
||||||
, СтруктураПараметров
|
, СтруктураПараметров
|
||||||
, ТокенВЗаголовки(Токен));
|
, ТокенВЗаголовки(Токен));
|
||||||
|
|
||||||
@ -354,7 +356,7 @@
|
|||||||
URL = "https://chatapi.viber.com/pa/send_message";
|
URL = "https://chatapi.viber.com/pa/send_message";
|
||||||
КонецЕсли;
|
КонецЕсли;
|
||||||
|
|
||||||
Ответ = OPI_Инструменты.PostUrlencoded(URL
|
Ответ = OPI_Инструменты.Post(URL
|
||||||
, СтруктураПараметров
|
, СтруктураПараметров
|
||||||
, ТокенВЗаголовки(Токен));
|
, ТокенВЗаголовки(Токен));
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
КонецФункции
|
КонецФункции
|
||||||
|
|
||||||
Функция Post(Знач URL
|
Функция PostMultipart(Знач URL
|
||||||
, Знач Параметры
|
, Знач Параметры
|
||||||
, Знач Файлы = ""
|
, Знач Файлы = ""
|
||||||
, Знач ТипКонтента = "image/jpeg"
|
, Знач ТипКонтента = "image/jpeg"
|
||||||
@ -147,16 +147,17 @@
|
|||||||
|
|
||||||
КонецФункции
|
КонецФункции
|
||||||
|
|
||||||
Функция PostUrlencoded(Знач URL, Знач Параметры = "", Знач ДопЗаголовки = "") Экспорт
|
Функция Post(Знач URL, Знач Параметры = "", Знач ДопЗаголовки = "", Знач JSON = Истина) Экспорт
|
||||||
|
|
||||||
Если Не ЗначениеЗаполнено(Параметры) Тогда
|
Если Не ЗначениеЗаполнено(Параметры) Тогда
|
||||||
Параметры = Новый Структура;
|
Параметры = Новый Структура;
|
||||||
КонецЕсли;
|
КонецЕсли;
|
||||||
|
|
||||||
|
ТипДанных = ?(JSON, "application/json", "application/x-www-form-urlencoded");
|
||||||
СтруктураURL = РазбитьURL(URL);
|
СтруктураURL = РазбитьURL(URL);
|
||||||
|
|
||||||
Заголовки = Новый Соответствие();
|
Заголовки = Новый Соответствие();
|
||||||
Заголовки.Вставить("Content-Type" , "application/x-www-form-urlencoded");
|
Заголовки.Вставить("Content-Type" , ТипДанных);
|
||||||
Заголовки.Вставить("Accept-Encoding" , "gzip");
|
Заголовки.Вставить("Accept-Encoding" , "gzip");
|
||||||
Заголовки.Вставить("Accept" , "*/*");
|
Заголовки.Вставить("Accept" , "*/*");
|
||||||
Заголовки.Вставить("Connection" , "keep-alive");
|
Заголовки.Вставить("Connection" , "keep-alive");
|
||||||
@ -172,16 +173,23 @@
|
|||||||
Соединение = Новый HTTPСоединение(СтруктураURL["Сервер"],443,,,,,Новый ЗащищенноеСоединениеOpenSSL());
|
Соединение = Новый HTTPСоединение(СтруктураURL["Сервер"],443,,,,,Новый ЗащищенноеСоединениеOpenSSL());
|
||||||
НовыйЗапрос = Новый HTTPЗапрос(СтруктураURL["Адрес"], Заголовки);
|
НовыйЗапрос = Новый HTTPЗапрос(СтруктураURL["Адрес"], Заголовки);
|
||||||
|
|
||||||
|
Если JSON Тогда
|
||||||
Данные = JSONСтрокой(Параметры);
|
Данные = JSONСтрокой(Параметры);
|
||||||
|
Иначе
|
||||||
|
СтрокаПараметров = ПараметрыЗапросаВСтроку(Параметры);
|
||||||
|
Данные = Прав(СтрокаПараметров, СтрДлина(СтрокаПараметров) - 1);
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
НовыйЗапрос.УстановитьТелоИзСтроки(Данные);
|
НовыйЗапрос.УстановитьТелоИзСтроки(Данные);
|
||||||
|
|
||||||
Ответ = Соединение.ВызватьHTTPМетод("POST", НовыйЗапрос);
|
Ответ = Соединение.ВызватьHTTPМетод("POST", НовыйЗапрос);
|
||||||
|
|
||||||
Если Ответ.Заголовки.Получить("Content-Encoding") <> Неопределено Тогда
|
НужнаРаспаковка = Ответ.Заголовки.Получить("Content-Encoding") = "gzip" Или
|
||||||
Если Ответ.Заголовки.Получить("Content-Encoding") = "gzip" Тогда
|
Ответ.Заголовки.Получить("content-encoding") = "gzip";
|
||||||
|
|
||||||
|
Если НужнаРаспаковка Тогда
|
||||||
Ответ = РаспаковатьОтвет(Ответ);
|
Ответ = РаспаковатьОтвет(Ответ);
|
||||||
КонецЕсли;
|
КонецЕсли;
|
||||||
КонецЕсли;
|
|
||||||
|
|
||||||
Возврат ?(ТипЗнч(Ответ) = Тип("ДвоичныеДанные"), JsonВСтруктуру(Ответ), Ответ);
|
Возврат ?(ТипЗнч(Ответ) = Тип("ДвоичныеДанные"), JsonВСтруктуру(Ответ), Ответ);
|
||||||
|
|
||||||
@ -225,6 +233,10 @@
|
|||||||
|
|
||||||
КонецПроцедуры
|
КонецПроцедуры
|
||||||
|
|
||||||
|
Функция UNIXTime(Знач Дата) Экспорт
|
||||||
|
Возврат Формат(Дата - Дата(1970,1,1,1,0,0), "ЧГ=0");
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
#КонецОбласти
|
#КонецОбласти
|
||||||
|
|
||||||
#Область Служебные
|
#Область Служебные
|
||||||
@ -291,80 +303,6 @@
|
|||||||
|
|
||||||
#КонецОбласти
|
#КонецОбласти
|
||||||
|
|
||||||
#Область БСП
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Copyright (c) 2019, ООО 1С-Софт
|
|
||||||
// Все права защищены. Эта программа и сопроводительные материалы предоставляются
|
|
||||||
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
|
|
||||||
// Текст лицензии доступен по ссылке:
|
|
||||||
// https://creativecommons.org/licenses/by/4.0/legalcode
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
Функция HMACSHA256(Знач Ключ, Знач Данные) Экспорт
|
|
||||||
|
|
||||||
Возврат HMAC(Ключ, Данные, ХешФункция.SHA256, 64);
|
|
||||||
|
|
||||||
КонецФункции
|
|
||||||
|
|
||||||
Функция Хеш(ДвоичныеДанные, Тип) Экспорт
|
|
||||||
|
|
||||||
Хеширование = Новый ХешированиеДанных(Тип);
|
|
||||||
Хеширование.Добавить(ДвоичныеДанные);
|
|
||||||
|
|
||||||
Возврат Хеширование.ХешСумма;
|
|
||||||
|
|
||||||
КонецФункции
|
|
||||||
|
|
||||||
Функция HMAC(Знач Ключ, Знач Данные, Тип, РазмерБлока) Экспорт
|
|
||||||
|
|
||||||
Если Ключ.Размер() > РазмерБлока Тогда
|
|
||||||
Ключ = Хеш(Ключ, Тип);
|
|
||||||
КонецЕсли;
|
|
||||||
|
|
||||||
Если Ключ.Размер() <= РазмерБлока Тогда
|
|
||||||
Ключ = ПолучитьHexСтрокуИзДвоичныхДанных(Ключ);
|
|
||||||
Ключ = Лев(Ключ + ПовторитьСтроку("00", РазмерБлока), РазмерБлока * 2);
|
|
||||||
КонецЕсли;
|
|
||||||
|
|
||||||
Ключ = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки(Ключ));
|
|
||||||
|
|
||||||
Ipad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("36", РазмерБлока));
|
|
||||||
Opad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("5c", РазмерБлока));
|
|
||||||
|
|
||||||
Ipad.ЗаписатьПобитовоеИсключительноеИли(0, Ключ);
|
|
||||||
Ikeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ipad);
|
|
||||||
|
|
||||||
Opad.ЗаписатьПобитовоеИсключительноеИли(0, Ключ);
|
|
||||||
Okeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(opad);
|
|
||||||
|
|
||||||
Возврат Хеш(СклеитьДвоичныеДанные(okeypad, Хеш(СклеитьДвоичныеДанные(ikeypad, Данные), Тип)), Тип);
|
|
||||||
|
|
||||||
КонецФункции
|
|
||||||
|
|
||||||
Функция СклеитьДвоичныеДанные(ДвоичныеДанные1, ДвоичныеДанные2) Экспорт
|
|
||||||
|
|
||||||
МассивДвоичныхДанных = Новый Массив;
|
|
||||||
МассивДвоичныхДанных.Добавить(ДвоичныеДанные1);
|
|
||||||
МассивДвоичныхДанных.Добавить(ДвоичныеДанные2);
|
|
||||||
|
|
||||||
Возврат СоединитьДвоичныеДанные(МассивДвоичныхДанных);
|
|
||||||
|
|
||||||
КонецФункции
|
|
||||||
|
|
||||||
Функция ПовторитьСтроку(Строка, Количество) Экспорт
|
|
||||||
|
|
||||||
Части = Новый Массив(Количество);
|
|
||||||
Для к = 1 По Количество Цикл
|
|
||||||
Части.Добавить(Строка);
|
|
||||||
КонецЦикла;
|
|
||||||
|
|
||||||
Возврат СтрСоединить(Части, "");
|
|
||||||
|
|
||||||
КонецФункции
|
|
||||||
|
|
||||||
#КонецОбласти
|
|
||||||
|
|
||||||
#КонецОбласти
|
#КонецОбласти
|
||||||
|
|
||||||
#Область СлужебныеПроцедурыИФункции
|
#Область СлужебныеПроцедурыИФункции
|
||||||
@ -399,20 +337,12 @@
|
|||||||
|
|
||||||
Функция РаспаковатьОтвет(Ответ)
|
Функция РаспаковатьОтвет(Ответ)
|
||||||
|
|
||||||
Заголовок = Ответ.Заголовки.Получить("Content-Encoding");
|
|
||||||
Если Заголовок <> Неопределено Тогда
|
|
||||||
Если НРег(Заголовок) = "gzip" Тогда
|
|
||||||
Возврат ПрочитатьGZip(Ответ.ПолучитьТелоКакДвоичныеДанные());
|
|
||||||
КонецЕсли;
|
|
||||||
КонецЕсли;
|
|
||||||
|
|
||||||
Попытка
|
Попытка
|
||||||
Возврат Ответ.Тело;
|
Возврат ПрочитатьGZip(Ответ.ПолучитьТелоКакДвоичныеДанные());
|
||||||
Исключение
|
Исключение
|
||||||
Возврат Ответ;
|
Возврат Ответ;
|
||||||
КонецПопытки;
|
КонецПопытки;
|
||||||
|
|
||||||
|
|
||||||
КонецФункции
|
КонецФункции
|
||||||
|
|
||||||
Функция ПрочитатьGZip(СжатыеДанные) Экспорт
|
Функция ПрочитатьGZip(СжатыеДанные) Экспорт
|
||||||
|
77
src/CommonModules/OPI_Криптография/Module.bsl
Normal file
77
src/CommonModules/OPI_Криптография/Module.bsl
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#Область СлужебныйПрограммныйИнтерфейс
|
||||||
|
|
||||||
|
#Область БСП
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright (c) 2019, ООО 1С-Софт
|
||||||
|
// Все права защищены. Эта программа и сопроводительные материалы предоставляются
|
||||||
|
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
|
||||||
|
// Текст лицензии доступен по ссылке:
|
||||||
|
// https://creativecommons.org/licenses/by/4.0/legalcode
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Функция HMACSHA256(Знач Ключ, Знач Данные) Экспорт
|
||||||
|
|
||||||
|
Возврат HMAC(Ключ, Данные, ХешФункция.SHA256, 64);
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
Функция Хеш(ДвоичныеДанные, Тип) Экспорт
|
||||||
|
|
||||||
|
Хеширование = Новый ХешированиеДанных(Тип);
|
||||||
|
Хеширование.Добавить(ДвоичныеДанные);
|
||||||
|
|
||||||
|
Возврат Хеширование.ХешСумма;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
Функция HMAC(Знач Ключ, Знач Данные, Тип, РазмерБлока) Экспорт
|
||||||
|
|
||||||
|
Если Ключ.Размер() > РазмерБлока Тогда
|
||||||
|
Ключ = Хеш(Ключ, Тип);
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
Если Ключ.Размер() <= РазмерБлока Тогда
|
||||||
|
Ключ = ПолучитьHexСтрокуИзДвоичныхДанных(Ключ);
|
||||||
|
Ключ = Лев(Ключ + ПовторитьСтроку("00", РазмерБлока), РазмерБлока * 2);
|
||||||
|
КонецЕсли;
|
||||||
|
|
||||||
|
Ключ = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки(Ключ));
|
||||||
|
|
||||||
|
Ipad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("36", РазмерБлока));
|
||||||
|
Opad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("5c", РазмерБлока));
|
||||||
|
|
||||||
|
Ipad.ЗаписатьПобитовоеИсключительноеИли(0, Ключ);
|
||||||
|
Ikeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ipad);
|
||||||
|
|
||||||
|
Opad.ЗаписатьПобитовоеИсключительноеИли(0, Ключ);
|
||||||
|
Okeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(opad);
|
||||||
|
|
||||||
|
Возврат Хеш(СклеитьДвоичныеДанные(okeypad, Хеш(СклеитьДвоичныеДанные(ikeypad, Данные), Тип)), Тип);
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
Функция СклеитьДвоичныеДанные(ДвоичныеДанные1, ДвоичныеДанные2) Экспорт
|
||||||
|
|
||||||
|
МассивДвоичныхДанных = Новый Массив;
|
||||||
|
МассивДвоичныхДанных.Добавить(ДвоичныеДанные1);
|
||||||
|
МассивДвоичныхДанных.Добавить(ДвоичныеДанные2);
|
||||||
|
|
||||||
|
Возврат СоединитьДвоичныеДанные(МассивДвоичныхДанных);
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
Функция ПовторитьСтроку(Строка, Количество) Экспорт
|
||||||
|
|
||||||
|
Части = Новый Массив(Количество);
|
||||||
|
Для к = 1 По Количество Цикл
|
||||||
|
Части.Добавить(Строка);
|
||||||
|
КонецЦикла;
|
||||||
|
|
||||||
|
Возврат СтрСоединить(Части, "");
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
#КонецОбласти
|
||||||
|
|
||||||
|
#КонецОбласти
|
11
src/CommonModules/OPI_Криптография/OPI_Криптография.mdo
Normal file
11
src/CommonModules/OPI_Криптография/OPI_Криптография.mdo
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mdclass:CommonModule xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass" uuid="331eccd4-9cf6-4b0c-8f24-2ad8b4f14d73">
|
||||||
|
<name>OPI_Криптография</name>
|
||||||
|
<synonym>
|
||||||
|
<key>ru</key>
|
||||||
|
<value>Криптография (OPI)</value>
|
||||||
|
</synonym>
|
||||||
|
<server>true</server>
|
||||||
|
<externalConnection>true</externalConnection>
|
||||||
|
<clientOrdinaryApplication>true</clientOrdinaryApplication>
|
||||||
|
</mdclass:CommonModule>
|
@ -37,7 +37,11 @@
|
|||||||
</languages>
|
</languages>
|
||||||
<commonModules>CommonModule.OPI_Инструменты</commonModules>
|
<commonModules>CommonModule.OPI_Инструменты</commonModules>
|
||||||
<commonModules>CommonModule.OPI_Telegram</commonModules>
|
<commonModules>CommonModule.OPI_Telegram</commonModules>
|
||||||
<commonModules>CommonModule.OPI_Viber</commonModules>
|
|
||||||
<commonModules>CommonModule.OPI_VK</commonModules>
|
<commonModules>CommonModule.OPI_VK</commonModules>
|
||||||
<commonModules>CommonModule.OPI_GoogleWorkspace</commonModules>
|
<commonModules>CommonModule.OPI_Viber</commonModules>
|
||||||
|
<commonModules>CommonModule.OPI_Twitter</commonModules>
|
||||||
|
<commonModules>CommonModule.OPI_Криптография</commonModules>
|
||||||
|
<httpServices>HTTPService.Twitter</httpServices>
|
||||||
|
<constants>Constant.TwitterRefresh</constants>
|
||||||
|
<constants>Constant.TwitterToken</constants>
|
||||||
</mdclass:Configuration>
|
</mdclass:Configuration>
|
||||||
|
23
src/Constants/TwitterRefresh/TwitterRefresh.mdo
Normal file
23
src/Constants/TwitterRefresh/TwitterRefresh.mdo
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mdclass:Constant xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:core="http://g5.1c.ru/v8/dt/mcore" xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass" uuid="c8f3e857-6526-4187-afea-598855ffde70">
|
||||||
|
<producedTypes>
|
||||||
|
<managerType typeId="baea1faa-8b19-499e-ad22-5e413b69a67f" valueTypeId="a131a492-e0e6-47b3-9501-18663d23439e"/>
|
||||||
|
<valueManagerType typeId="a1110732-e4b7-4bdc-abb6-26df3df9d506" valueTypeId="a187fa98-56fe-4e8b-b8fe-08ccf78e17f6"/>
|
||||||
|
<valueKeyType typeId="47ba4824-aaa2-451b-a122-b1f6f121ade8" valueTypeId="b1f60de8-1aef-4acc-937a-7210e5567c1e"/>
|
||||||
|
</producedTypes>
|
||||||
|
<name>TwitterRefresh</name>
|
||||||
|
<synonym>
|
||||||
|
<key>ru</key>
|
||||||
|
<value>Twitter refresh</value>
|
||||||
|
</synonym>
|
||||||
|
<type>
|
||||||
|
<types>String</types>
|
||||||
|
<stringQualifiers>
|
||||||
|
<length>200</length>
|
||||||
|
</stringQualifiers>
|
||||||
|
</type>
|
||||||
|
<useStandardCommands>true</useStandardCommands>
|
||||||
|
<minValue xsi:type="core:UndefinedValue"/>
|
||||||
|
<maxValue xsi:type="core:UndefinedValue"/>
|
||||||
|
<dataLockControlMode>Managed</dataLockControlMode>
|
||||||
|
</mdclass:Constant>
|
23
src/Constants/TwitterToken/TwitterToken.mdo
Normal file
23
src/Constants/TwitterToken/TwitterToken.mdo
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mdclass:Constant xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:core="http://g5.1c.ru/v8/dt/mcore" xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass" uuid="b96c0e0e-6a2b-4f9c-a9f9-68007b23fced">
|
||||||
|
<producedTypes>
|
||||||
|
<managerType typeId="6021e728-01fc-42c0-8ccc-1c0b7bd8381f" valueTypeId="2bced781-db0a-456a-8f96-6d1a91540478"/>
|
||||||
|
<valueManagerType typeId="ac3557ea-4133-4581-bae4-f46a6b1a0c1f" valueTypeId="defeb168-3985-42b5-8e29-67a58470a221"/>
|
||||||
|
<valueKeyType typeId="fef49910-3ad6-4d99-b0c4-c82708438d2a" valueTypeId="8098c190-1889-4a13-86b4-0bcd805ab200"/>
|
||||||
|
</producedTypes>
|
||||||
|
<name>TwitterToken</name>
|
||||||
|
<synonym>
|
||||||
|
<key>ru</key>
|
||||||
|
<value>Twitter token</value>
|
||||||
|
</synonym>
|
||||||
|
<type>
|
||||||
|
<types>String</types>
|
||||||
|
<stringQualifiers>
|
||||||
|
<length>200</length>
|
||||||
|
</stringQualifiers>
|
||||||
|
</type>
|
||||||
|
<useStandardCommands>true</useStandardCommands>
|
||||||
|
<minValue xsi:type="core:UndefinedValue"/>
|
||||||
|
<maxValue xsi:type="core:UndefinedValue"/>
|
||||||
|
<dataLockControlMode>Managed</dataLockControlMode>
|
||||||
|
</mdclass:Constant>
|
18
src/HTTPServices/Twitter/Module.bsl
Normal file
18
src/HTTPServices/Twitter/Module.bsl
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
Функция Twitterget(Запрос)
|
||||||
|
|
||||||
|
Код = Запрос.ПараметрыЗапроса["code"];
|
||||||
|
ОтветТокен = OPI_Twitter.ПолучитьТокен(Код);
|
||||||
|
|
||||||
|
Константы.TwitterRefresh.Установить(ОтветТокен["refresh_token"]);
|
||||||
|
Константы.TwitterToken.Установить(ОтветТокен["access_token"]);
|
||||||
|
|
||||||
|
Ответ = Новый HTTPСервисОтвет(200);
|
||||||
|
Возврат Ответ;
|
||||||
|
|
||||||
|
КонецФункции
|
||||||
|
|
||||||
|
Функция Twitterpost(Запрос)
|
||||||
|
Ответ = Новый HTTPСервисОтвет(200);
|
||||||
|
Возврат Ответ;
|
||||||
|
КонецФункции
|
36
src/HTTPServices/Twitter/Twitter.mdo
Normal file
36
src/HTTPServices/Twitter/Twitter.mdo
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mdclass:HTTPService xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass" uuid="2a9ba3e3-8cfe-43b9-ae44-6b35fd3f70b4">
|
||||||
|
<name>Twitter</name>
|
||||||
|
<synonym>
|
||||||
|
<key>ru</key>
|
||||||
|
<value>Twitter</value>
|
||||||
|
</synonym>
|
||||||
|
<rootURL>twitter</rootURL>
|
||||||
|
<reuseSessions>AutoUse</reuseSessions>
|
||||||
|
<sessionMaxAge>20</sessionMaxAge>
|
||||||
|
<urlTemplates uuid="72f0e476-8cae-4ee4-8d24-38113a27c8a3">
|
||||||
|
<name>Twitter</name>
|
||||||
|
<synonym>
|
||||||
|
<key>ru</key>
|
||||||
|
<value>Twitter</value>
|
||||||
|
</synonym>
|
||||||
|
<template>/*</template>
|
||||||
|
<methods uuid="4073c6bb-6d05-40d5-890d-18ea834f52e0">
|
||||||
|
<name>get</name>
|
||||||
|
<synonym>
|
||||||
|
<key>ru</key>
|
||||||
|
<value>Get</value>
|
||||||
|
</synonym>
|
||||||
|
<handler>Twitterget</handler>
|
||||||
|
</methods>
|
||||||
|
<methods uuid="28dde02e-477e-43a4-b1a7-e097a58b3da1">
|
||||||
|
<name>post</name>
|
||||||
|
<synonym>
|
||||||
|
<key>ru</key>
|
||||||
|
<value>Post</value>
|
||||||
|
</synonym>
|
||||||
|
<httpMethod>POST</httpMethod>
|
||||||
|
<handler>Twitterpost</handler>
|
||||||
|
</methods>
|
||||||
|
</urlTemplates>
|
||||||
|
</mdclass:HTTPService>
|
Loading…
Reference in New Issue
Block a user