Deprecate Twitter #14

Open
missytake wants to merge 5 commits from deprecate-twitter into development
36 changed files with 6 additions and 1536 deletions

View file

@ -87,21 +87,6 @@ query_mailaddr SELECT 1 FROM email WHERE ? IN (name || '@kibicara.example.com');
```
- Don't forget to restart OpenSMTPd when you change your database: `rcctl stop && rcctl start`
#### Configure Twitter
Twitter needs you to create a Twitter App, which hood admins can permit to read and write messages.
- Create Twitter account and app: https://developer.twitter.com
- Get your customer key and customer secret and append this to `/etc/kibicara.conf`:
```
[twitter]
consumer_key = '<your_consumer_key>'
consumer_secret = '<your_consumer_secret>'
```
- You need to configure a Callback Url in your Twitter App:
- Go to: `https://developer.twitter.com/en/apps`
- Add `https://kibicara.example.com/dashboard/twitter-callback` as Callback Url of your Twitter App. This is needed to successfully create a twitter oauth handshake.
#### Configure Telegram
Nothing to do, because telegram has a nice API.

View file

@ -1,164 +0,0 @@
# Copyright (C) 2020 by Cathy Hu <cathy.hu@fau.de>
# Copyright (C) 2020 by Martin Rey <martin.rey@mailbox.org>
#
# SPDX-License-Identifier: 0BSD
from asyncio import CancelledError, gather, sleep
from logging import getLogger
from peony import PeonyClient, exceptions
from kibicara.config import config
from kibicara.platformapi import Censor, Message, Spawner
from kibicara.platforms.twitter.model import Twitter
logger = getLogger(__name__)
class TwitterBot(Censor):
def __init__(self, twitter_model):
super().__init__(twitter_model.hood)
self.twitter_model = twitter_model
self.enabled = self.twitter_model.enabled
self.polling_interval_sec = 60
self.mentions_since_id = self.twitter_model.mentions_since_id
self.dms_since_id = self.twitter_model.dms_since_id
@classmethod
async def destroy_hood(cls, hood):
"""Removes all its database entries."""
for twitter in await Twitter.objects.filter(hood=hood).all():
await twitter.delete()
async def run(self):
try:
if not self.twitter_model.verified:
raise ValueError("Oauth Handshake not completed")
self.client = PeonyClient(
consumer_key=config["twitter"]["consumer_key"],
consumer_secret=config["twitter"]["consumer_secret"],
access_token=self.twitter_model.access_token,
access_token_secret=self.twitter_model.access_token_secret,
)
if self.twitter_model.mentions_since_id is None:
logger.debug("since_id is None in model, fetch newest mention id")
await self._poll_mentions()
if self.twitter_model.dms_since_id is None:
logger.debug("since_id is None in model, fetch newest dm id")
await self._poll_direct_messages()
user = await self.client.user
if user.screen_name:
await self.twitter_model.update(username=user.screen_name)
logger.debug(
"Starting Twitter bot: {0}".format(self.twitter_model.__dict__)
)
await gather(self.poll(), self.push())
except CancelledError:
logger.debug(
"Bot {0} received Cancellation.".format(self.twitter_model.hood.name)
)
except exceptions.Unauthorized:
logger.debug(
"Bot {0} has invalid auth token.".format(self.twitter_model.hood.name)
)
await self.twitter_model.update(enabled=False)
self.enabled = self.twitter_model.enabled
except (KeyError, ValueError, exceptions.NotAuthenticated):
logger.warning("Missing consumer_keys for Twitter in your configuration.")
await self.twitter_model.update(enabled=False)
self.enabled = self.twitter_model.enabled
finally:
logger.debug("Bot {0} stopped.".format(self.twitter_model.hood.name))
async def poll(self):
while True:
dms = await self._poll_direct_messages()
logger.debug(
"Polled dms ({0}): {1}".format(self.twitter_model.hood.name, str(dms))
)
mentions = await self._poll_mentions()
logger.debug(
"Polled mentions ({0}): {1}".format(
self.twitter_model.hood.name, str(mentions)
)
)
await self.twitter_model.update(
dms_since_id=self.dms_since_id, mentions_since_id=self.mentions_since_id
)
for message in dms:
await self.publish(Message(message))
for message_id, message in mentions:
await self.publish(Message(message, twitter_mention_id=message_id))
await sleep(self.polling_interval_sec)
async def _poll_direct_messages(self):
dms = await self.client.api.direct_messages.events.list.get()
dms = dms.events
# TODO check for next_cursor (see twitter api)
dms_filtered = []
if dms:
for dm in dms:
if int(dm.id) == self.dms_since_id:
break
dms_filtered.append(dm)
self.dms_since_id = int(dms[0].id)
messages = []
for dm in dms_filtered:
filtered_text = await self._filter_text(
dm.message_create.message_data.entities,
dm.message_create.message_data.text,
)
if not filtered_text:
continue
messages.append(filtered_text)
return messages
async def _poll_mentions(self):
mentions = await self.client.api.statuses.mentions_timeline.get(
since_id=self.mentions_since_id
)
if mentions:
self.mentions_since_id = mentions[0].id
messages = []
for mention in mentions:
filtered_text = await self._filter_text(mention.entities, mention.text)
if not filtered_text:
continue
messages.append((mention.id, filtered_text))
return messages
async def _filter_text(self, entities, text):
remove_indices = set()
for user in entities.user_mentions:
remove_indices.update(range(user.indices[0], user.indices[1] + 1))
for url in entities.urls:
remove_indices.update(range(url.indices[0], url.indices[1] + 1))
for symbol in entities.symbols:
remove_indices.update(range(symbol.indices[0], symbol.indices[1] + 1))
filtered_text = ""
for index, character in enumerate(text):
if index not in remove_indices:
filtered_text += character
return filtered_text.strip()
async def push(self):
while True:
message = await self.receive()
logger.debug(
"Received message from censor ({0}): {1}".format(
self.twitter_model.hood.name, message.text
)
)
if hasattr(message, "twitter_mention_id"):
await self._retweet(message.twitter_mention_id)
else:
await self._post_tweet(message.text)
async def _post_tweet(self, message):
return await self.client.api.statuses.update.post(status=message)
async def _retweet(self, message_id):
return await self.client.api.statuses.retweet.post(id=message_id)
spawner = Spawner(Twitter, TwitterBot)

View file

@ -1,23 +0,0 @@
# Copyright (C) 2020 by Cathy Hu <cathy.hu@fau.de>
# Copyright (C) 2020 by Martin Rey <martin.rey@mailbox.org>
#
# SPDX-License-Identifier: 0BSD
from ormantic import Boolean, ForeignKey, Integer, Model, Text
from kibicara.model import Hood, Mapping
class Twitter(Model):
id: Integer(primary_key=True) = None
hood: ForeignKey(Hood)
dms_since_id: Integer(allow_null=True) = None
mentions_since_id: Integer(allow_null=True) = None
access_token: Text()
access_token_secret: Text()
username: Text(allow_null=True) = None
verified: Boolean() = False
enabled: Boolean() = False
class Mapping(Mapping):
table_name = "twitterbots"

View file

@ -1,183 +0,0 @@
# Copyright (C) 2020 by Cathy Hu <cathy.hu@fau.de>
# Copyright (C) 2020 by Martin Rey <martin.rey@mailbox.org>
#
# SPDX-License-Identifier: 0BSD
from logging import getLogger
from sqlite3 import IntegrityError
from fastapi import APIRouter, Depends, HTTPException, Response, status
from ormantic.exceptions import NoMatch
from peony.exceptions import NotAuthenticated
from peony.oauth_dance import get_access_token, get_oauth_token
from pydantic import BaseModel
from kibicara.config import config
from kibicara.platforms.twitter.bot import spawner
from kibicara.platforms.twitter.model import Twitter
from kibicara.webapi.hoods import get_hood, get_hood_unauthorized
logger = getLogger(__name__)
class BodyTwitterPublic(BaseModel):
username: str
async def get_twitter(twitter_id: int, hood=Depends(get_hood)):
try:
return await Twitter.objects.get(id=twitter_id, hood=hood)
except NoMatch:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
router = APIRouter()
twitter_callback_router = APIRouter()
@router.get(
"/public",
# TODO response_model,
operation_id="get_twitters_public",
)
async def twitter_read_all_public(hood=Depends(get_hood_unauthorized)):
twitterbots = await Twitter.objects.filter(hood=hood).all()
return [
BodyTwitterPublic(username=twitterbot.username)
for twitterbot in twitterbots
if twitterbot.verified == 1 and twitterbot.enabled == 1 and twitterbot.username
]
@router.get(
"/",
# TODO response_model,
operation_id="get_twitters",
)
async def twitter_read_all(hood=Depends(get_hood)):
return await Twitter.objects.filter(hood=hood).all()
@router.get(
"/{twitter_id}",
# TODO response_model
operation_id="get_twitter",
)
async def twitter_read(twitter=Depends(get_twitter)):
return twitter
@router.delete(
"/{twitter_id}",
status_code=status.HTTP_204_NO_CONTENT,
# TODO response_model
operation_id="delete_twitter",
)
async def twitter_delete(twitter=Depends(get_twitter)):
spawner.stop(twitter)
await twitter.delete()
return Response(status_code=status.HTTP_204_NO_CONTENT)
@router.get(
"/{twitter_id}/status",
status_code=status.HTTP_200_OK,
# TODO response_model
operation_id="status_twitter",
)
async def twitter_status(twitter=Depends(get_twitter)):
return {"status": spawner.get(twitter).status.name}
@router.post(
"/{twitter_id}/start",
status_code=status.HTTP_200_OK,
# TODO response_model
operation_id="start_twitter",
)
async def twitter_start(twitter=Depends(get_twitter)):
await twitter.update(enabled=True)
spawner.get(twitter).start()
return {}
@router.post(
"/{twitter_id}/stop",
status_code=status.HTTP_200_OK,
# TODO response_model
operation_id="stop_twitter",
)
async def twitter_stop(twitter=Depends(get_twitter)):
await twitter.update(enabled=False)
spawner.get(twitter).stop()
return {}
@router.post(
"/",
status_code=status.HTTP_201_CREATED,
# TODO response_model
operation_id="create_twitter",
)
async def twitter_create(response: Response, hood=Depends(get_hood)):
"""
`https://api.twitter.com/oauth/authorize?oauth_token=`
"""
try:
# Purge Twitter corpses
for corpse in await Twitter.objects.filter(hood=hood, verified=False).all():
await corpse.delete()
# Create Twitter
request_token = await get_oauth_token(
config["twitter"]["consumer_key"],
config["twitter"]["consumer_secret"],
callback_uri="{0}/dashboard/twitter-callback?hood={1}".format(
config["frontend_url"], hood.id
),
)
if request_token["oauth_callback_confirmed"] != "true":
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE)
twitter = await Twitter.objects.create(
hood=hood,
access_token=request_token["oauth_token"],
access_token_secret=request_token["oauth_token_secret"],
)
response.headers["Location"] = str(twitter.id)
return twitter
except IntegrityError:
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
except (KeyError, ValueError, NotAuthenticated):
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
@twitter_callback_router.get(
"/callback",
# TODO response_model
operation_id="callback_twitter",
)
async def twitter_read_callback(
oauth_token: str, oauth_verifier: str, hood=Depends(get_hood)
):
try:
twitter = await Twitter.objects.filter(access_token=oauth_token).get()
access_token = await get_access_token(
config["twitter"]["consumer_key"],
config["twitter"]["consumer_secret"],
twitter.access_token,
twitter.access_token_secret,
oauth_verifier,
)
await twitter.update(
access_token=access_token["oauth_token"],
access_token_secret=access_token["oauth_token_secret"],
verified=True,
enabled=True,
)
spawner.start(twitter)
return {}
except IntegrityError:
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
except NoMatch:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
except (KeyError, ValueError, NotAuthenticated):
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)

View file

@ -16,8 +16,6 @@ from kibicara.platforms.email.webapi import router as email_router
from kibicara.platforms.telegram.webapi import router as telegram_router
from kibicara.platforms.test.webapi import router as test_router
from kibicara.platforms.mastodon.webapi import router as mastodon_router
from kibicara.platforms.twitter.webapi import router as twitter_router
from kibicara.platforms.twitter.webapi import twitter_callback_router
from kibicara.webapi.admin import router as admin_router
from kibicara.webapi.hoods import router as hoods_router
from kibicara.webapi.hoods.badwords import router as badwords_router
@ -35,12 +33,8 @@ hoods_router.include_router(test_router, prefix="/{hood_id}/test", tags=["test"]
hoods_router.include_router(
telegram_router, prefix="/{hood_id}/telegram", tags=["telegram"]
)
hoods_router.include_router(
twitter_router, prefix="/{hood_id}/twitter", tags=["twitter"]
)
hoods_router.include_router(
mastodon_router, prefix="/{hood_id}/mastodon", tags=["mastodon"]
)
router.include_router(twitter_callback_router, prefix="/twitter", tags=["twitter"])
hoods_router.include_router(email_router, prefix="/{hood_id}/email", tags=["email"])
router.include_router(hoods_router, prefix="/hoods")

View file

@ -10,7 +10,6 @@ import { MastodonService } from './api/mastodon.service';
import { TelegramService } from './api/telegram.service';
import { TestService } from './api/test.service';
import { TriggersService } from './api/triggers.service';
import { TwitterService } from './api/twitter.service';
@NgModule({
imports: [],

View file

@ -14,6 +14,4 @@ export * from './test.service';
import { TestService } from './test.service';
export * from './triggers.service';
import { TriggersService } from './triggers.service';
export * from './twitter.service';
import { TwitterService } from './twitter.service';
export const APIS = [AdminService, BadwordsService, EmailService, HoodsService, MastodonService, TelegramService, TestService, TriggersService, TwitterService];
export const APIS = [AdminService, BadwordsService, EmailService, HoodsService, MastodonService, TelegramService, TestService, TriggersService];

View file

@ -1,595 +0,0 @@
/**
* FastAPI
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 0.1.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/* tslint:disable:no-unused-variable member-ordering */
import { Inject, Injectable, Optional } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams,
HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http';
import { CustomHttpParameterCodec } from '../encoder';
import { Observable } from 'rxjs';
import { HTTPValidationError } from '../model/models';
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
@Injectable({
providedIn: 'root'
})
export class TwitterService {
protected basePath = 'http://localhost/api';
public defaultHeaders = new HttpHeaders();
public configuration = new Configuration();
public encoder: HttpParameterCodec;
constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) {
if (configuration) {
this.configuration = configuration;
}
if (typeof this.configuration.basePath !== 'string') {
if (typeof basePath !== 'string') {
basePath = this.basePath;
}
this.configuration.basePath = basePath;
}
this.encoder = this.configuration.encoder || new CustomHttpParameterCodec();
}
private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams {
if (typeof value === "object" && value instanceof Date === false) {
httpParams = this.addToHttpParamsRecursive(httpParams, value);
} else {
httpParams = this.addToHttpParamsRecursive(httpParams, value, key);
}
return httpParams;
}
private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams {
if (value == null) {
return httpParams;
}
if (typeof value === "object") {
if (Array.isArray(value)) {
(value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key));
} else if (value instanceof Date) {
if (key != null) {
httpParams = httpParams.append(key,
(value as Date).toISOString().substr(0, 10));
} else {
throw Error("key may not be null if value is Date");
}
} else {
Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive(
httpParams, value[k], key != null ? `${key}.${k}` : k));
}
} else if (key != null) {
httpParams = httpParams.append(key, value);
} else {
throw Error("key may not be null if value is not object or array");
}
return httpParams;
}
/**
* Twitter Read Callback
* @param oauthToken
* @param oauthVerifier
* @param hoodId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public callbackTwitter(oauthToken: string, oauthVerifier: string, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<any>;
public callbackTwitter(oauthToken: string, oauthVerifier: string, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<any>>;
public callbackTwitter(oauthToken: string, oauthVerifier: string, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<any>>;
public callbackTwitter(oauthToken: string, oauthVerifier: string, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
if (oauthToken === null || oauthToken === undefined) {
throw new Error('Required parameter oauthToken was null or undefined when calling callbackTwitter.');
}
if (oauthVerifier === null || oauthVerifier === undefined) {
throw new Error('Required parameter oauthVerifier was null or undefined when calling callbackTwitter.');
}
if (hoodId === null || hoodId === undefined) {
throw new Error('Required parameter hoodId was null or undefined when calling callbackTwitter.');
}
let queryParameters = new HttpParams({encoder: this.encoder});
if (oauthToken !== undefined && oauthToken !== null) {
queryParameters = this.addToHttpParams(queryParameters,
<any>oauthToken, 'oauth_token');
}
if (oauthVerifier !== undefined && oauthVerifier !== null) {
queryParameters = this.addToHttpParams(queryParameters,
<any>oauthVerifier, 'oauth_verifier');
}
if (hoodId !== undefined && hoodId !== null) {
queryParameters = this.addToHttpParams(queryParameters,
<any>hoodId, 'hood_id');
}
let headers = this.defaultHeaders;
let credential: string | undefined;
// authentication (OAuth2PasswordBearer) required
credential = this.configuration.lookupCredential('OAuth2PasswordBearer');
if (credential) {
headers = headers.set('Authorization', 'Bearer ' + credential);
}
let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
if (httpHeaderAcceptSelected === undefined) {
// to determine the Accept header
const httpHeaderAccepts: string[] = [
'application/json'
];
httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
}
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.get<any>(`${this.configuration.basePath}/api/twitter/callback`,
{
params: queryParameters,
responseType: <any>responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
/**
* Twitter Create
* &#x60;https://api.twitter.com/oauth/authorize?oauth_token&#x3D;&#x60;
* @param hoodId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public createTwitter(hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<any>;
public createTwitter(hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<any>>;
public createTwitter(hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<any>>;
public createTwitter(hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
if (hoodId === null || hoodId === undefined) {
throw new Error('Required parameter hoodId was null or undefined when calling createTwitter.');
}
let headers = this.defaultHeaders;
let credential: string | undefined;
// authentication (OAuth2PasswordBearer) required
credential = this.configuration.lookupCredential('OAuth2PasswordBearer');
if (credential) {
headers = headers.set('Authorization', 'Bearer ' + credential);
}
let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
if (httpHeaderAcceptSelected === undefined) {
// to determine the Accept header
const httpHeaderAccepts: string[] = [
'application/json'
];
httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
}
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.post<any>(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/`,
null,
{
responseType: <any>responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
/**
* Twitter Delete
* @param twitterId
* @param hoodId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public deleteTwitter(twitterId: number, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<any>;
public deleteTwitter(twitterId: number, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<any>>;
public deleteTwitter(twitterId: number, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<any>>;
public deleteTwitter(twitterId: number, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
if (twitterId === null || twitterId === undefined) {
throw new Error('Required parameter twitterId was null or undefined when calling deleteTwitter.');
}
if (hoodId === null || hoodId === undefined) {
throw new Error('Required parameter hoodId was null or undefined when calling deleteTwitter.');
}
let headers = this.defaultHeaders;
let credential: string | undefined;
// authentication (OAuth2PasswordBearer) required
credential = this.configuration.lookupCredential('OAuth2PasswordBearer');
if (credential) {
headers = headers.set('Authorization', 'Bearer ' + credential);
}
let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
if (httpHeaderAcceptSelected === undefined) {
// to determine the Accept header
const httpHeaderAccepts: string[] = [
'application/json'
];
httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
}
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.delete<any>(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`,
{
responseType: <any>responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
/**
* Twitter Read
* @param twitterId
* @param hoodId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public getTwitter(twitterId: number, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<any>;
public getTwitter(twitterId: number, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<any>>;
public getTwitter(twitterId: number, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<any>>;
public getTwitter(twitterId: number, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
if (twitterId === null || twitterId === undefined) {
throw new Error('Required parameter twitterId was null or undefined when calling getTwitter.');
}
if (hoodId === null || hoodId === undefined) {
throw new Error('Required parameter hoodId was null or undefined when calling getTwitter.');
}
let headers = this.defaultHeaders;
let credential: string | undefined;
// authentication (OAuth2PasswordBearer) required
credential = this.configuration.lookupCredential('OAuth2PasswordBearer');
if (credential) {
headers = headers.set('Authorization', 'Bearer ' + credential);
}
let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
if (httpHeaderAcceptSelected === undefined) {
// to determine the Accept header
const httpHeaderAccepts: string[] = [
'application/json'
];
httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
}
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.get<any>(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`,
{
responseType: <any>responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
/**
* Twitter Read All
* @param hoodId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public getTwitters(hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<any>;
public getTwitters(hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<any>>;
public getTwitters(hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<any>>;
public getTwitters(hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
if (hoodId === null || hoodId === undefined) {
throw new Error('Required parameter hoodId was null or undefined when calling getTwitters.');
}
let headers = this.defaultHeaders;
let credential: string | undefined;
// authentication (OAuth2PasswordBearer) required
credential = this.configuration.lookupCredential('OAuth2PasswordBearer');
if (credential) {
headers = headers.set('Authorization', 'Bearer ' + credential);
}
let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
if (httpHeaderAcceptSelected === undefined) {
// to determine the Accept header
const httpHeaderAccepts: string[] = [
'application/json'
];
httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
}
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.get<any>(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/`,
{
responseType: <any>responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
/**
* Twitter Read All Public
* @param hoodId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public getTwittersPublic(hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<any>;
public getTwittersPublic(hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<any>>;
public getTwittersPublic(hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<any>>;
public getTwittersPublic(hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
if (hoodId === null || hoodId === undefined) {
throw new Error('Required parameter hoodId was null or undefined when calling getTwittersPublic.');
}
let headers = this.defaultHeaders;
let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
if (httpHeaderAcceptSelected === undefined) {
// to determine the Accept header
const httpHeaderAccepts: string[] = [
'application/json'
];
httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
}
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.get<any>(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/public`,
{
responseType: <any>responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
/**
* Twitter Start
* @param twitterId
* @param hoodId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public startTwitter(twitterId: number, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<any>;
public startTwitter(twitterId: number, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<any>>;
public startTwitter(twitterId: number, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<any>>;
public startTwitter(twitterId: number, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
if (twitterId === null || twitterId === undefined) {
throw new Error('Required parameter twitterId was null or undefined when calling startTwitter.');
}
if (hoodId === null || hoodId === undefined) {
throw new Error('Required parameter hoodId was null or undefined when calling startTwitter.');
}
let headers = this.defaultHeaders;
let credential: string | undefined;
// authentication (OAuth2PasswordBearer) required
credential = this.configuration.lookupCredential('OAuth2PasswordBearer');
if (credential) {
headers = headers.set('Authorization', 'Bearer ' + credential);
}
let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
if (httpHeaderAcceptSelected === undefined) {
// to determine the Accept header
const httpHeaderAccepts: string[] = [
'application/json'
];
httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
}
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.post<any>(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/start`,
null,
{
responseType: <any>responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
/**
* Twitter Status
* @param twitterId
* @param hoodId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public statusTwitter(twitterId: number, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<any>;
public statusTwitter(twitterId: number, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<any>>;
public statusTwitter(twitterId: number, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<any>>;
public statusTwitter(twitterId: number, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
if (twitterId === null || twitterId === undefined) {
throw new Error('Required parameter twitterId was null or undefined when calling statusTwitter.');
}
if (hoodId === null || hoodId === undefined) {
throw new Error('Required parameter hoodId was null or undefined when calling statusTwitter.');
}
let headers = this.defaultHeaders;
let credential: string | undefined;
// authentication (OAuth2PasswordBearer) required
credential = this.configuration.lookupCredential('OAuth2PasswordBearer');
if (credential) {
headers = headers.set('Authorization', 'Bearer ' + credential);
}
let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
if (httpHeaderAcceptSelected === undefined) {
// to determine the Accept header
const httpHeaderAccepts: string[] = [
'application/json'
];
httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
}
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.get<any>(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/status`,
{
responseType: <any>responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
/**
* Twitter Stop
* @param twitterId
* @param hoodId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public stopTwitter(twitterId: number, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<any>;
public stopTwitter(twitterId: number, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<any>>;
public stopTwitter(twitterId: number, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<any>>;
public stopTwitter(twitterId: number, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
if (twitterId === null || twitterId === undefined) {
throw new Error('Required parameter twitterId was null or undefined when calling stopTwitter.');
}
if (hoodId === null || hoodId === undefined) {
throw new Error('Required parameter hoodId was null or undefined when calling stopTwitter.');
}
let headers = this.defaultHeaders;
let credential: string | undefined;
// authentication (OAuth2PasswordBearer) required
credential = this.configuration.lookupCredential('OAuth2PasswordBearer');
if (credential) {
headers = headers.set('Authorization', 'Bearer ' + credential);
}
let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
if (httpHeaderAcceptSelected === undefined) {
// to determine the Accept header
const httpHeaderAccepts: string[] = [
'application/json'
];
httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
}
if (httpHeaderAcceptSelected !== undefined) {
headers = headers.set('Accept', httpHeaderAcceptSelected);
}
let responseType: 'text' | 'json' = 'json';
if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) {
responseType = 'text';
}
return this.httpClient.post<any>(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/stop`,
null,
{
responseType: <any>responseType,
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress
}
);
}
}

View file

@ -49,8 +49,8 @@
<p>
<strong>Share the hood page: </strong> Just send the link to your hood
page to your community. We also recommend to link it in your platform
account description (e.g. Twitter description) to give the users
context and more information.
account description (e.g. Mastodon account description) to give the
users context and more information.
</p>
</mat-expansion-panel>
</mat-accordion>

View file

@ -37,7 +37,6 @@
<div class="platforms-container">
<app-email-settings [hoodId]="hoodId"></app-email-settings>
<app-twitter-settings [hoodId]="hoodId"></app-twitter-settings>
<app-telegram-settings [hoodId]="hoodId"></app-telegram-settings>
<app-mastodon-settings [hoodId]="hoodId"></app-mastodon-settings>
</div>

View file

@ -4,7 +4,6 @@ import { DashboardComponent } from './dashboard.component';
import { HoodsComponent } from './hoods/hoods.component';
import { AuthGuard } from '../core/auth/auth.guard';
import { BoardComponent } from './board/board.component';
import { TwitterCallbackComponent } from '../platforms/twitter/twitter-callback/twitter-callback.component';
import { AccountSettingsComponent } from './account-settings/account-settings.component';
const routes: Routes = [
@ -15,8 +14,6 @@ const routes: Routes = [
{ path: '', component: HoodsComponent },
{ path: 'hoods/:id', component: BoardComponent },
{ path: 'settings', component: AccountSettingsComponent },
// Platform-specific Routes
{ path: 'twitter-callback', component: TwitterCallbackComponent },
],
canActivate: [AuthGuard],
},

View file

@ -1,6 +1,5 @@
<h2>Platforms - choose one, get all hood messages</h2>
<div class="container">
<app-twitter-bot-card [hoodId]="hoodId"></app-twitter-bot-card>
<app-telegram-bot-card [hoodId]="hoodId"></app-telegram-bot-card>
<app-email-bot-card [hoodId]="hoodId"></app-email-bot-card>
<app-mastodon-bot-card [hoodId]="hoodId"></app-mastodon-bot-card>

View file

@ -2,22 +2,16 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TelegramSettingsComponent } from './telegram/telegram-settings/telegram-settings.component';
import { SharedModule } from '../shared/shared.module';
import { TwitterSettingsComponent } from './twitter/twitter-settings/twitter-settings.component';
import { EmailSettingsComponent } from './email/email-settings/email-settings.component';
import { EmailDialogComponent } from './email/email-settings/email-dialog/email-dialog.component';
import { EmailInfoDialogComponent } from './email/email-settings/email-info-dialog/email-info-dialog.component';
import { TelegramInfoDialogComponent } from './telegram/telegram-settings/telegram-info-dialog/telegram-info-dialog.component';
import { TelegramDialogComponent } from './telegram/telegram-settings/telegram-dialog/telegram-dialog.component';
import { TwitterInfoDialogComponent } from './twitter/twitter-settings/twitter-info-dialog/twitter-info-dialog.component';
import { TwitterCallbackComponent } from './twitter/twitter-callback/twitter-callback.component';
import { TwitterCorpsesPipe } from './twitter/twitter-corpses-pipe/twitter-corpses.pipe';
import { PlatformsInfoPageComponent } from './platforms-info-page/platforms-info-page.component';
import { EmailBotCardComponent } from './email/email-bot-card/email-bot-card.component';
import { TelegramBotCardComponent } from './telegram/telegram-bot-card/telegram-bot-card.component';
import { TwitterBotCardComponent } from './twitter/twitter-bot-card/twitter-bot-card.component';
import { EmailBotInfoDialogComponent } from './email/email-bot-card/email-bot-info-dialog/email-bot-info-dialog.component';
import { TelegramBotInfoDialogComponent } from './telegram/telegram-bot-card/telegram-bot-info-dialog/telegram-bot-info-dialog.component';
import { TwitterBotInfoDialogComponent } from './twitter/twitter-bot-card/twitter-bot-info-dialog/twitter-bot-info-dialog.component';
import { EmailConfirmationComponent } from './email/email-confirmation/email-confirmation.component';
import { EmailUnsubscribeComponent } from './email/email-unsubscribe/email-unsubscribe.component';
import { MastodonBotCardComponent } from './mastodon/mastodon-bot-card/mastodon-bot-card.component';
@ -28,22 +22,16 @@ import { MastodonBotInfoDialogComponent } from './mastodon/mastodon-bot-card/mas
@NgModule({
declarations: [
TelegramSettingsComponent,
TwitterSettingsComponent,
EmailSettingsComponent,
EmailDialogComponent,
EmailInfoDialogComponent,
TelegramInfoDialogComponent,
TelegramDialogComponent,
TwitterInfoDialogComponent,
TwitterCallbackComponent,
TwitterCorpsesPipe,
PlatformsInfoPageComponent,
EmailBotCardComponent,
TelegramBotCardComponent,
TwitterBotCardComponent,
EmailBotInfoDialogComponent,
TelegramBotInfoDialogComponent,
TwitterBotInfoDialogComponent,
EmailConfirmationComponent,
EmailUnsubscribeComponent,
MastodonBotCardComponent,
@ -55,7 +43,6 @@ import { MastodonBotInfoDialogComponent } from './mastodon/mastodon-bot-card/mas
exports: [
TelegramSettingsComponent,
MastodonSettingsComponent,
TwitterSettingsComponent,
EmailSettingsComponent,
PlatformsInfoPageComponent,
],

View file

@ -1,46 +0,0 @@
<div *ngIf="twitters$ | loading | async as twitters">
<ng-template [ngIf]="twitters.value">
<mat-card appearance="outlined">
<mat-card-header>
<div mat-card-avatar class="twitter"></div>
<mat-card-title class="platform-title">
Twitter
<button mat-icon-button aria-label="How to use">
<mat-icon
matTooltip="How to send and receive hood broadcast messages with twitter"
class="info-button"
(click)="onInfoClick()"
>info</mat-icon
>
</button>
</mat-card-title>
</mat-card-header>
<mat-card-content *ngIf="twitters.value.length !== 0; else noTwitter">
<mat-selection-list [multiple]="false" class="list">
<a
*ngFor="let twitter of twitters.value"
href="https://twitter.com/{{ twitter.username }}"
routerLinkActive="router-link-active"
>
<mat-list-option>
@{{ twitter.username }}
<mat-divider></mat-divider>
</mat-list-option>
</a>
</mat-selection-list>
</mat-card-content>
</mat-card>
<ng-template #noTwitter>
<mat-card-content>
Unfortunately your hood admin has not configured Twitter as platform
yet.
</mat-card-content>
</ng-template>
</ng-template>
<ng-template [ngIf]="twitters.error"
><mat-icon class="warning">warning</mat-icon></ng-template
>
<ng-template [ngIf]="twitters.loading">
<mat-spinner [diameter]="45" class="spinner"></mat-spinner>
</ng-template>
</div>

View file

@ -1,11 +0,0 @@
.twitter {
background-image: url("../../../../assets/twitter.png");
background-size: cover;
}
.platform-title {
display: grid;
grid-template-columns: 1fr 40px;
width: 100%;
align-items: center;
}

View file

@ -1,24 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { TwitterBotCardComponent } from './twitter-bot-card.component';
describe('TwitterBotCardComponent', () => {
let component: TwitterBotCardComponent;
let fixture: ComponentFixture<TwitterBotCardComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [TwitterBotCardComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TwitterBotCardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,27 +0,0 @@
import { Component, OnInit, Input } from '@angular/core';
import { TwitterService } from 'src/app/core/api';
import { TwitterBotInfoDialogComponent } from './twitter-bot-info-dialog/twitter-bot-info-dialog.component';
import { MatDialog } from '@angular/material/dialog';
@Component({
selector: 'app-twitter-bot-card',
templateUrl: './twitter-bot-card.component.html',
styleUrls: ['./twitter-bot-card.component.scss'],
})
export class TwitterBotCardComponent implements OnInit {
@Input() hoodId;
twitters$;
constructor(
private twitterService: TwitterService,
private dialog: MatDialog
) {}
ngOnInit(): void {
this.twitters$ = this.twitterService.getTwittersPublic(this.hoodId);
}
onInfoClick() {
this.dialog.open(TwitterBotInfoDialogComponent);
}
}

View file

@ -1,51 +0,0 @@
<mat-dialog-content>
<div class="container">
<h2>How to communicate with the hood via Twitter?</h2>
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title
>How to subscribe to the hood via Twitter?</mat-panel-title
>
</mat-expansion-panel-header>
<p>
Click on the twitter bot name that is shown in the twitter card. Just
follow the twitter account that the link leads to.
</p>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title
>How to send a broadcast message to the hood?</mat-panel-title
>
</mat-expansion-panel-header>
<p>You have two options:</p>
<ul>
<li>
Mention the bot in your tweet. The message of your tweet will be
broadcasted.
</li>
<li>
Write a direct message to the bot. The message will be broadcasted.
</li>
</ul>
<p>Both options have the equal result.</p>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>How to receive messages?</mat-panel-title>
</mat-expansion-panel-header>
<p>
Follow one of the twitter accounts in the twitter card. It will
broadcast all messages it receives by tweeting the content.
</p>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>How to stop receiving messages?</mat-panel-title>
</mat-expansion-panel-header>
<p>Just unfollow the twitter accounts that you previously followed.</p>
</mat-expansion-panel>
</mat-accordion>
</div>
</mat-dialog-content>

View file

@ -1,4 +0,0 @@
.container {
margin-top: 10px;
margin-bottom: 10px;
}

View file

@ -1,12 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-twitter-bot-info-dialog',
templateUrl: './twitter-bot-info-dialog.component.html',
styleUrls: ['./twitter-bot-info-dialog.component.scss'],
})
export class TwitterBotInfoDialogComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
}

View file

@ -1,3 +0,0 @@
<div>
<mat-spinner class="spinner"></mat-spinner>
</div>

View file

@ -1,4 +0,0 @@
.spinner {
display: block;
margin: auto;
}

View file

@ -1,24 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { TwitterCallbackComponent } from './twitter-callback.component';
describe('TwitterCallbackComponent', () => {
let component: TwitterCallbackComponent;
let fixture: ComponentFixture<TwitterCallbackComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [TwitterCallbackComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TwitterCallbackComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,47 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { TwitterService } from 'src/app/core/api';
@Component({
selector: 'app-twitter-callback',
templateUrl: './twitter-callback.component.html',
styleUrls: ['./twitter-callback.component.scss'],
})
export class TwitterCallbackComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private router: Router,
private twitterService: TwitterService
) {}
ngOnInit(): void {
if (
this.route.snapshot.queryParams.hood &&
this.route.snapshot.queryParams.oauth_token &&
this.route.snapshot.queryParams.oauth_verifier
) {
this.twitterService
.callbackTwitter(
this.route.snapshot.queryParams.oauth_token,
this.route.snapshot.queryParams.oauth_verifier,
this.route.snapshot.queryParams.hood
)
.subscribe(() => {
this.router.navigate([
'/dashboard/hoods',
this.route.snapshot.queryParams.hood,
]);
});
} else if (
this.route.snapshot.queryParams.hood &&
this.route.snapshot.queryParams.denied
) {
this.router.navigate([
'/dashboard/hoods',
this.route.snapshot.queryParams.hood,
]);
} else {
this.router.navigate(['/404']);
}
}
}

View file

@ -1,10 +0,0 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'twitterCorpses',
})
export class TwitterCorpsesPipe implements PipeTransform {
transform(twitters) {
return twitters.filter((x) => x.verified === 1);
}
}

View file

@ -1,41 +0,0 @@
<mat-dialog-content>
<h2>How to add an twitter bot to your hood</h2>
<h3>What is a twitter bot?</h3>
<p>
Twitter bots are twitter accounts operated by software. In our case we will
show you how to add a bot for users to subscribe to, so they can send
messages into your hood system by mentioning the bot or direct messaging it,
which get distributed to the other platforms. Also they can receive messages
from other platforms by reading the tweets of the bot.
</p>
<p>
<strong>Example: </strong> You as a hood admin added the bot
@kibicara-my-hood to your hood. John wants to send a message to all hood
subscribers. He then uses his twitter account to follow to the bot
@kibicara-my-hood and sends his message to the bot by sending a tweet. His
message will be distributed to the other platforms and Sandra who is
subscribed to your email hood bot gets John's message via email.
</p>
<h3>How to add a twitter bot to your hood?</h3>
<ol>
<li>
<a href="https://twitter.com" target="_blank">Create a twitter account</a>
for the twitter bot and log in. We recommend <strong>NOT</strong> to use
your own personal twitter account but to create a new seperate hood
account since you will give full access to the account to our software.
</li>
<li>
Click on the <strong>Add a new platform connection!</strong>-Button. This
will redirect you to the twitter page to authorize access to the twitter
account. Click the
<strong>Authorize app</strong>
Button. You will be redirected back here. Done!
</li>
</ol>
<img class="example-image" src="assets/auth-app-twitter.png" />
<h3>Can I stop relaying twitter messages?</h3>
<p>
Yes, if you want to stop relaying twitter messages from a specific twitter
bot, just use the switch to the bot to stop relaying.
</p>
</mat-dialog-content>

View file

@ -1,9 +0,0 @@
.example-image {
margin-left: 10%;
margin-right: 10%;
@media screen and (max-width: 600px) {
width: 100%;
margin-left: 0%;
margin-right: 0%;
}
}

View file

@ -1,12 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-twitter-info-dialog',
templateUrl: './twitter-info-dialog.component.html',
styleUrls: ['./twitter-info-dialog.component.scss'],
})
export class TwitterInfoDialogComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
}

View file

@ -1,64 +0,0 @@
<mat-card appearance="outlined">
<mat-card-header>
<div mat-card-avatar class="twitter"></div>
<mat-card-title class="platform-title">
Twitter
<button mat-icon-button aria-label="How to use">
<mat-icon
matTooltip="How to add an twitter bot to your hood"
class="info-button"
(click)="onInfoClick()"
>info</mat-icon
>
</button>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-list *ngIf="twitters$ | loading | async as twitters">
<ng-template [ngIf]="twitters.value">
<mat-list-item *ngIf="(twitters.value | twitterCorpses).length === 0">
<button class="add-button" mat-button (click)="onCreate()">
<div class="in-add-button">
<mat-icon>add</mat-icon>
<span> Add a platform connection!</span>
</div>
</button>
<mat-divider></mat-divider>
</mat-list-item>
<mat-list-item *ngFor="let twitter of twitters.value | twitterCorpses">
<div class="entry">
@{{ twitter.username }}
<mat-slide-toggle
[checked]="twitter.enabled === 1"
(change)="onChange(twitter)"
></mat-slide-toggle>
<button
mat-icon-button
[matMenuTriggerFor]="menu"
aria-label="Example icon-button with a menu"
>
<mat-icon>more_vert</mat-icon>
</button>
</div>
<mat-divider></mat-divider>
<mat-menu #menu="matMenu">
<button mat-menu-item (click)="onDelete(twitter.id)">
<mat-icon>delete</mat-icon>
<span>Delete</span>
</button>
<button mat-menu-item (click)="onCreate()">
<mat-icon>add</mat-icon>
<span>Add another</span>
</button>
</mat-menu>
</mat-list-item>
</ng-template>
<ng-template [ngIf]="twitters.error"
><mat-icon class="warning">warning</mat-icon></ng-template
>
<ng-template [ngIf]="twitters.loading">
<mat-spinner [diameter]="45" class="spinner"></mat-spinner>
</ng-template>
</mat-list>
</mat-card-content>
</mat-card>

View file

@ -1,22 +0,0 @@
.entry {
display: grid;
grid-template-columns: 4fr 40px 20px;
width: 100%;
align-items: center;
}
.platform-title {
display: grid;
grid-template-columns: 1fr 40px;
width: 100%;
align-items: center;
}
.platform-heading {
align-self: flex-end;
}
.twitter {
background-image: url("../../../../assets/twitter.png");
background-size: cover;
}

View file

@ -1,24 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { TwitterSettingsComponent } from './twitter-settings.component';
describe('TwitterSettingsComponent', () => {
let component: TwitterSettingsComponent;
let fixture: ComponentFixture<TwitterSettingsComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [TwitterSettingsComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TwitterSettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,77 +0,0 @@
import { Component, OnInit, Input } from '@angular/core';
import { Observable } from 'rxjs';
import { TwitterService } from 'src/app/core/api';
import { TwitterInfoDialogComponent } from './twitter-info-dialog/twitter-info-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-twitter-settings',
templateUrl: './twitter-settings.component.html',
styleUrls: ['./twitter-settings.component.scss'],
})
export class TwitterSettingsComponent implements OnInit {
@Input() hoodId;
twitters$: Observable<Array<any>>;
constructor(
private twitterService: TwitterService,
public dialog: MatDialog,
private snackBar: MatSnackBar
) {}
ngOnInit(): void {
this.reload();
}
private reload() {
this.twitters$ = this.twitterService.getTwitters(this.hoodId);
}
onInfoClick() {
this.dialog.open(TwitterInfoDialogComponent);
}
onDelete(twitterId) {
this.twitterService.deleteTwitter(twitterId, this.hoodId).subscribe(() => {
this.reload();
});
}
onCreate() {
this.twitterService.createTwitter(this.hoodId).subscribe((twitter) => {
if (twitter && twitter.access_token) {
const redirectUrl =
'https://api.twitter.com/oauth/authorize?oauth_token=' +
twitter.access_token;
window.location.href = redirectUrl;
}
});
}
onChange(twitter) {
if (twitter.enabled === 0) {
this.twitterService.startTwitter(twitter.id, this.hoodId).subscribe(
() => {},
(error) => {
this.snackBar.open('Could not start. Check your settings.', 'Close', {
duration: 2000,
});
}
);
} else if (twitter.enabled === 1) {
this.twitterService.stopTwitter(twitter.id, this.hoodId).subscribe(
() => {},
(error) => {
this.snackBar.open('Could not stop. Check your settings.', 'Close', {
duration: 2000,
});
}
);
}
// TODO yeah i know this is bad, implement disabling/enabling
setTimeout(() => {
this.reload();
}, 100);
}
}

View file

@ -112,11 +112,6 @@
use. If you are a normal user:
</p>
<ul>
<li>
Twitter: We save the id of the last received message (mentions and
direct messages). This can be a message of yours if you sent the
latest message.
</li>
<li>Telegram: We save your telegram username.</li>
<li>E-Mail: We save your e-mail address.</li>
</ul>
@ -130,12 +125,6 @@
<p>For hood admins:</p>
<ul>
<li>We save your e-mail address and your password.</li>
<li>
Twitter: We save oauth tokens provided by twitter that give us access
to the twitter account and the username of the twitter account. We
disadvice the use of your private twitter account for acting as
Kibicara platform bot. Please create a new twitter account.
</li>
<li>
Telegram: We save the telegram API token to the provided telegram bot
and a welcome message. Also we retrieve the name of the telegram bot.

View file

@ -31,7 +31,7 @@
<div class="big-paragraph-font">
<h2 class="big-h2-font">Inclusive and easy</h2>
<p>
Messages sent with Kibicara reach people via Telegram, Twitter and even
Messages sent with Kibicara reach people via Telegram, Mastodon and even
E-Mail - perfect for announcing the next neighborhood meetings, creating
news tickers or even providing disaster warnings.
</p>
@ -47,7 +47,7 @@
community-administrated instances of Kibicara, being able to customize
different platform accounts and filters. You subscribe to a Kibicara hood
through your service of choice. This can either be E-Mail, Telegram or
Twitter.
Mastodon.
</p>
<a mat-raised-button [routerLink]="['/hoods']">Discover hoods!</a>
</div>

View file

@ -6,7 +6,7 @@
#
# client-side git-hook - checks commit message style
pattern='\[(core|frontend|twitter|telegram|email|xmpp|mastodon|tests|doc|misc)\] [[:upper:]].*[^.]'
pattern='\[(core|frontend|telegram|email|xmpp|mastodon|tests|doc|misc)\] [[:upper:]].*[^.]'
head -n 1 "$1" | egrep -x "$pattern" > /dev/null
if [ $? -ne 0 ]; then
echo "commit message doesn't match \"$pattern\"" >&2