Browse Source

Remove twitter notification configuration (#2598)

pull/2608/head
Michael David Kuckuk 2 years ago committed by GitHub
parent
commit
59e5cfefd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 25
      controllers/admin/notifications.go
  2. 2
      controllers/admin/serverConfig.go
  3. 22
      core/data/config.go
  4. 1
      docs/dependencies.md
  5. 1
      go.mod
  6. 2
      go.sum
  7. 11
      models/notification.go
  8. 41
      notifications/notifications.go
  9. 78
      notifications/twitter/twitter.go
  10. 1
      router/router.go
  11. 220
      web/components/admin/notification/twitter.tsx
  12. 8
      web/pages/admin/config-notify.tsx
  13. 11
      web/types/config-section.ts
  14. 45
      web/utils/config-constants.tsx
  15. 9
      web/utils/server-status-context.tsx

25
controllers/admin/notifications.go

@ -58,28 +58,3 @@ func SetBrowserNotificationConfiguration(w http.ResponseWriter, r *http.Request) @@ -58,28 +58,3 @@ func SetBrowserNotificationConfiguration(w http.ResponseWriter, r *http.Request)
controllers.WriteSimpleResponse(w, true, "updated browser push config with provided values")
}
// SetTwitterConfiguration will set the browser notification configuration.
func SetTwitterConfiguration(w http.ResponseWriter, r *http.Request) {
if !requirePOST(w, r) {
return
}
type request struct {
Value models.TwitterConfiguration `json:"value"`
}
decoder := json.NewDecoder(r.Body)
var config request
if err := decoder.Decode(&config); err != nil {
controllers.WriteSimpleResponse(w, false, "unable to update twitter config with provided values")
return
}
if err := data.SetTwitterConfiguration(config.Value); err != nil {
controllers.WriteSimpleResponse(w, false, "unable to update twitter config with provided values")
return
}
controllers.WriteSimpleResponse(w, true, "updated twitter config with provided values")
}

2
controllers/admin/serverConfig.go

@ -84,7 +84,6 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) { @@ -84,7 +84,6 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
Notifications: notificationsConfigResponse{
Discord: data.GetDiscordConfig(),
Browser: data.GetBrowserPushConfig(),
Twitter: data.GetTwitterConfiguration(),
},
}
@ -160,5 +159,4 @@ type federationConfigResponse struct { @@ -160,5 +159,4 @@ type federationConfigResponse struct {
type notificationsConfigResponse struct {
Browser models.BrowserNotificationConfiguration `json:"browser"`
Discord models.DiscordConfiguration `json:"discord"`
Twitter models.TwitterConfiguration `json:"twitter"`
}

22
core/data/config.go

@ -63,7 +63,6 @@ const ( @@ -63,7 +63,6 @@ const (
browserPushConfigurationKey = "browser_push_configuration"
browserPushPublicKeyKey = "browser_push_public_key"
browserPushPrivateKeyKey = "browser_push_private_key"
twitterConfigurationKey = "twitter_configuration"
hasConfiguredInitialNotificationsKey = "has_configured_initial_notifications"
hideViewerCountKey = "hide_viewer_count"
customOfflineMessageKey = "custom_offline_message"
@ -880,27 +879,6 @@ func GetBrowserPushPrivateKey() (string, error) { @@ -880,27 +879,6 @@ func GetBrowserPushPrivateKey() (string, error) {
return _datastore.GetString(browserPushPrivateKeyKey)
}
// SetTwitterConfiguration will set the Twitter configuration.
func SetTwitterConfiguration(config models.TwitterConfiguration) error {
configEntry := ConfigEntry{Key: twitterConfigurationKey, Value: config}
return _datastore.Save(configEntry)
}
// GetTwitterConfiguration will return the Twitter configuration.
func GetTwitterConfiguration() models.TwitterConfiguration {
configEntry, err := _datastore.Get(twitterConfigurationKey)
if err != nil {
return models.TwitterConfiguration{Enabled: false}
}
var config models.TwitterConfiguration
if err := configEntry.getObject(&config); err != nil {
return models.TwitterConfiguration{Enabled: false}
}
return config
}
// SetHasPerformedInitialNotificationsConfig sets when performed initial setup.
func SetHasPerformedInitialNotificationsConfig(hasConfigured bool) error {
return _datastore.SetBool(hasConfiguredInitialNotificationsKey, true)

1
docs/dependencies.md

@ -56,7 +56,6 @@ Owncast Dependencies @@ -56,7 +56,6 @@ Owncast Dependencies
- HTTP routing https://github.com/gorilla/mux
- Mastodon API https://github.com/mattn/go-mastodon
- Twitter API https://github.com/ChimeraCoder/anaconda
- Go ORM https://gorm.io/gorm
- ID string generator https://github.com/teris-io/shortid
- Slug generator https://github.com/metal3d/go-slugify

1
go.mod

@ -77,7 +77,6 @@ require github.com/SherClockHolmes/webpush-go v1.2.0 @@ -77,7 +77,6 @@ require github.com/SherClockHolmes/webpush-go v1.2.0
require (
github.com/aymerick/douceur v0.2.0 // indirect
github.com/dghubble/oauth1 v0.7.2
github.com/g8rswimmer/go-twitter/v2 v2.1.5
github.com/go-test/deep v1.0.4 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/gorilla/css v1.0.0 // indirect

2
go.sum

@ -70,8 +70,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF @@ -70,8 +70,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/g8rswimmer/go-twitter/v2 v2.1.5 h1:Uj9Yuof2UducrP4Xva7irnUJfB9354/VyUXKmc2D5gg=
github.com/g8rswimmer/go-twitter/v2 v2.1.5/go.mod h1:/55xWb313KQs25X7oZrNSEwLQNkYHhPsDwFstc45vhc=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM=

11
models/notification.go

@ -14,14 +14,3 @@ type BrowserNotificationConfiguration struct { @@ -14,14 +14,3 @@ type BrowserNotificationConfiguration struct {
Enabled bool `json:"enabled"`
GoLiveMessage string `json:"goLiveMessage,omitempty"`
}
// TwitterConfiguration represents the configuration for Twitter access.
type TwitterConfiguration struct {
Enabled bool `json:"enabled"`
APIKey string `json:"apiKey"` // aka consumer key
APISecret string `json:"apiSecret"` // aka consumer secret
AccessToken string `json:"accessToken"`
AccessTokenSecret string `json:"accessTokenSecret"`
BearerToken string `json:"bearerToken"`
GoLiveMessage string `json:"goLiveMessage,omitempty"`
}

41
notifications/notifications.go

@ -2,15 +2,12 @@ package notifications @@ -2,15 +2,12 @@ package notifications
import (
"fmt"
"strings"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/notifications/browser"
"github.com/owncast/owncast/notifications/discord"
"github.com/owncast/owncast/notifications/twitter"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
@ -20,7 +17,6 @@ type Notifier struct { @@ -20,7 +17,6 @@ type Notifier struct {
datastore *data.Datastore
browser *browser.Browser
discord *discord.Discord
twitter *twitter.Twitter
}
// Setup will perform any pre-use setup for the notifier.
@ -68,9 +64,6 @@ func New(datastore *data.Datastore) (*Notifier, error) { @@ -68,9 +64,6 @@ func New(datastore *data.Datastore) (*Notifier, error) {
if err := notifier.setupDiscord(); err != nil {
log.Error(err)
}
if err := notifier.setupTwitter(); err != nil {
log.Errorln(err)
}
return &notifier, nil
}
@ -147,36 +140,6 @@ func (n *Notifier) notifyDiscord() { @@ -147,36 +140,6 @@ func (n *Notifier) notifyDiscord() {
}
}
func (n *Notifier) setupTwitter() error {
if twitterConfig := data.GetTwitterConfiguration(); twitterConfig.Enabled {
if t, err := twitter.New(twitterConfig.APIKey, twitterConfig.APISecret, twitterConfig.AccessToken, twitterConfig.AccessTokenSecret, twitterConfig.BearerToken); err == nil {
n.twitter = t
} else if err != nil {
return errors.Wrap(err, "error creating twitter notifier")
}
}
return nil
}
func (n *Notifier) notifyTwitter() {
goLiveMessage := data.GetTwitterConfiguration().GoLiveMessage
streamTitle := data.GetStreamTitle()
if streamTitle != "" {
goLiveMessage += "\n" + streamTitle
}
tagString := ""
for _, tag := range utils.ShuffleStringSlice(data.GetServerMetadataTags()) {
tagString = fmt.Sprintf("%s #%s", tagString, tag)
}
tagString = strings.TrimSpace(tagString)
message := fmt.Sprintf("%s\n%s\n\n%s", goLiveMessage, data.GetServerURL(), tagString)
if err := n.twitter.Notify(message); err != nil {
log.Errorln("error sending twitter message", err)
}
}
// Notify will fire the different notification channels.
func (n *Notifier) Notify() {
if n.browser != nil {
@ -186,8 +149,4 @@ func (n *Notifier) Notify() { @@ -186,8 +149,4 @@ func (n *Notifier) Notify() {
if n.discord != nil {
n.notifyDiscord()
}
if n.twitter != nil {
n.notifyTwitter()
}
}

78
notifications/twitter/twitter.go

@ -1,78 +0,0 @@ @@ -1,78 +0,0 @@
package twitter
import (
"context"
"errors"
"fmt"
"net/http"
"github.com/dghubble/oauth1"
"github.com/g8rswimmer/go-twitter/v2"
)
/*
1. developer.twitter.com. Apply to be a developer if needed.
2. Projects and apps -> Your project name
3. Settings.
4. Scroll down to"User authentication settings" Edit
5. Enable OAuth 1.0a with Read/Write permissions.
6. Fill out the form with your information. Callback can be anything.
7. Go to your project "Keys and tokens"
8. Generate API key and secret.
9. Generate access token and secret. Verify it says "Read and write permissions."
10. Generate bearer token.
*/
type authorize struct {
Token string
}
func (a authorize) Add(req *http.Request) {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Token))
}
// Twitter is an instance of the Twitter notifier.
type Twitter struct {
apiKey string
apiSecret string
accessToken string
accessTokenSecret string
bearerToken string
}
// New returns a new instance of the Twitter notifier.
func New(apiKey, apiSecret, accessToken, accessTokenSecret, bearerToken string) (*Twitter, error) {
if apiKey == "" || apiSecret == "" || accessToken == "" || accessTokenSecret == "" || bearerToken == "" {
return nil, errors.New("missing some or all of the required twitter configuration values")
}
return &Twitter{
apiKey: apiKey,
apiSecret: apiSecret,
accessToken: accessToken,
accessTokenSecret: accessTokenSecret,
bearerToken: bearerToken,
}, nil
}
// Notify will send a notification to Twitter with the supplied text.
func (t *Twitter) Notify(text string) error {
config := oauth1.NewConfig(t.apiKey, t.apiSecret)
token := oauth1.NewToken(t.accessToken, t.accessTokenSecret)
httpClient := config.Client(oauth1.NoContext, token)
client := &twitter.Client{
Authorizer: authorize{
Token: t.bearerToken,
},
Client: httpClient,
Host: "https://api.twitter.com",
}
req := twitter.CreateTweetRequest{
Text: text,
}
_, err := client.CreateTweet(context.Background(), req)
return err
}

1
router/router.go

@ -366,7 +366,6 @@ func Start() error { @@ -366,7 +366,6 @@ func Start() error {
// Configure outbound notification channels.
http.HandleFunc("/api/admin/config/notifications/discord", middleware.RequireAdminAuth(admin.SetDiscordNotificationConfiguration))
http.HandleFunc("/api/admin/config/notifications/browser", middleware.RequireAdminAuth(admin.SetBrowserNotificationConfiguration))
http.HandleFunc("/api/admin/config/notifications/twitter", middleware.RequireAdminAuth(admin.SetTwitterConfiguration))
// Auth

220
web/components/admin/notification/twitter.tsx

@ -1,220 +0,0 @@ @@ -1,220 +0,0 @@
import { Button, Typography } from 'antd';
import React, { useState, useContext, useEffect } from 'react';
import { ServerStatusContext } from '../../../utils/server-status-context';
import { TextField, TEXTFIELD_TYPE_PASSWORD } from '../TextField';
import { FormStatusIndicator } from '../FormStatusIndicator';
import {
postConfigUpdateToAPI,
RESET_TIMEOUT,
TWITTER_CONFIG_FIELDS,
} from '../../../utils/config-constants';
import { ToggleSwitch } from '../ToggleSwitch';
import {
createInputStatus,
StatusState,
STATUS_ERROR,
STATUS_SUCCESS,
} from '../../../utils/input-statuses';
import { UpdateArgs } from '../../../types/config-section';
import { TEXTFIELD_TYPE_TEXT } from '../TextFieldWithSubmit';
const { Title } = Typography;
export const ConfigNotify = () => {
const serverStatusData = useContext(ServerStatusContext);
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
const { notifications } = serverConfig || {};
const { twitter } = notifications || {};
const [formDataValues, setFormDataValues] = useState<any>({});
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
const [enableSaveButton, setEnableSaveButton] = useState<boolean>(false);
useEffect(() => {
const {
enabled,
apiKey,
apiSecret,
accessToken,
accessTokenSecret,
bearerToken,
goLiveMessage,
} = twitter || {};
setFormDataValues({
enabled,
apiKey,
apiSecret,
accessToken,
accessTokenSecret,
bearerToken,
goLiveMessage,
});
}, [twitter]);
const canSave = (): boolean => {
const { apiKey, apiSecret, accessToken, accessTokenSecret, bearerToken, goLiveMessage } =
formDataValues;
return (
!!apiKey &&
!!apiSecret &&
!!accessToken &&
!!accessTokenSecret &&
!!bearerToken &&
!!goLiveMessage
);
};
useEffect(() => {
setEnableSaveButton(canSave());
}, [formDataValues]);
// update individual values in state
const handleFieldChange = ({ fieldName, value }: UpdateArgs) => {
setFormDataValues({
...formDataValues,
[fieldName]: value,
});
};
// toggle switch.
const handleSwitchChange = (switchEnabled: boolean) => {
const previouslySaved = formDataValues.enabled;
handleFieldChange({ fieldName: 'enabled', value: switchEnabled });
return switchEnabled !== previouslySaved;
};
let resetTimer = null;
const resetStates = () => {
setSubmitStatus(null);
resetTimer = null;
clearTimeout(resetTimer);
setEnableSaveButton(false);
};
const save = async () => {
const postValue = formDataValues;
await postConfigUpdateToAPI({
apiPath: '/notifications/twitter',
data: { value: postValue },
onSuccess: () => {
setFieldInConfigState({
fieldName: 'twitter',
value: postValue,
path: 'notifications',
});
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Updated.'));
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
},
onError: (message: string) => {
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
},
});
};
return (
<>
<Title>Twitter</Title>
<p className="description reduced-margins">
Let your Twitter followers know each time you go live.
</p>
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
<p className="description reduced-margins">
<a href="https://owncast.online/docs/notifications" target="_blank" rel="noreferrer">
Read how to configure your Twitter account
</a>{' '}
to support posting from Owncast.
</p>
<p className="description reduced-margins">
<a
href="https://developer.twitter.com/en/portal/dashboard"
target="_blank"
rel="noreferrer"
>
And then get your Twitter developer credentials
</a>{' '}
to fill in below.
</p>
</div>
<ToggleSwitch
apiPath=""
fieldName="enabled"
label="Enable Twitter"
onChange={handleSwitchChange}
checked={formDataValues.enabled}
/>
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
<TextField
{...TWITTER_CONFIG_FIELDS.apiKey}
required
value={formDataValues.apiKey}
onChange={handleFieldChange}
/>
</div>
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
<TextField
{...TWITTER_CONFIG_FIELDS.apiSecret}
type={TEXTFIELD_TYPE_PASSWORD}
required
value={formDataValues.apiSecret}
onChange={handleFieldChange}
/>
</div>
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
<TextField
{...TWITTER_CONFIG_FIELDS.accessToken}
required
value={formDataValues.accessToken}
onChange={handleFieldChange}
/>
</div>
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
<TextField
{...TWITTER_CONFIG_FIELDS.accessTokenSecret}
type={TEXTFIELD_TYPE_PASSWORD}
required
value={formDataValues.accessTokenSecret}
onChange={handleFieldChange}
/>
</div>
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
<TextField
{...TWITTER_CONFIG_FIELDS.bearerToken}
required
value={formDataValues.bearerToken}
onChange={handleFieldChange}
/>
</div>
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
<TextField
{...TWITTER_CONFIG_FIELDS.goLiveMessage}
type={TEXTFIELD_TYPE_TEXT}
required
value={formDataValues.goLiveMessage}
onChange={handleFieldChange}
/>
</div>
<Button
type="primary"
onClick={save}
style={{
display: enableSaveButton ? 'inline-block' : 'none',
position: 'relative',
marginLeft: 'auto',
right: '0',
marginTop: '20px',
}}
>
Save
</Button>
<FormStatusIndicator status={submitStatus} />
</>
);
};
export default ConfigNotify;

8
web/pages/admin/config-notify.tsx

@ -4,7 +4,6 @@ import Link from 'next/link'; @@ -4,7 +4,6 @@ import Link from 'next/link';
import Discord from '../../components/admin/notification/discord';
import Browser from '../../components/admin/notification/browser';
import Twitter from '../../components/admin/notification/twitter';
import Federation from '../../components/admin/notification/federation';
import {
TextFieldWithSubmit,
@ -99,13 +98,6 @@ export default function ConfigNotify() { @@ -99,13 +98,6 @@ export default function ConfigNotify() {
>
<Browser />
</Col>
<Col
span={10}
className={`form-module ${enabled ? '' : 'disabled'}`}
style={{ margin: '5px', display: 'flex', flexDirection: 'column' }}
>
<Twitter />
</Col>
<Col
span={10}

11
web/types/config-section.ts

@ -115,20 +115,9 @@ export interface DiscordNotification { @@ -115,20 +115,9 @@ export interface DiscordNotification {
goLiveMessage: string;
}
export interface TwitterNotification {
enabled: boolean;
apiKey: string;
apiSecret: string;
accessToken: string;
accessTokenSecret: string;
bearerToken: string;
goLiveMessage: string;
}
export interface NotificationsConfig {
browser: BrowserNotification;
discord: DiscordNotification;
twitter: TwitterNotification;
}
export interface Health {

45
web/utils/config-constants.tsx

@ -557,48 +557,3 @@ export const BROWSER_PUSH_CONFIG_FIELDS = { @@ -557,48 +557,3 @@ export const BROWSER_PUSH_CONFIG_FIELDS = {
placeholder: `I've gone live! Come watch!`,
},
};
export const TWITTER_CONFIG_FIELDS = {
apiKey: {
fieldName: 'apiKey',
label: 'API Key',
maxLength: 200,
tip: '',
placeholder: `gaUQhRC2lqfrEFfElBXJgOctU`,
},
apiSecret: {
fieldName: 'apiSecret',
label: 'API Secret',
maxLength: 200,
tip: '',
placeholder: `IIz4jFZMWbUKdFOEGUprFjRwIslG56d1SPQlolJYjXwJ2y2qKS`,
},
accessToken: {
fieldName: 'accessToken',
label: 'Access Token',
maxLength: 200,
tip: '',
placeholder: `952540400-EEiwe9fkuSvWjnNC82YFa9kgpqbyAP3J7FjE2dkka`,
},
accessTokenSecret: {
fieldName: 'accessTokenSecret',
label: 'Access Token Secret',
maxLength: 200,
tip: '',
placeholder: `xO0AZWNGfZxpNsYPg3zNEKhAsPPGvNZFlzQArA2khI9Kg`,
},
bearerToken: {
fieldName: 'bearerToken',
label: 'Bearer Token',
maxLength: 200,
tip: '',
placeholder: `AAAAAAAAAAAAAAFqpXwEAAnnepHkjA8XD5ftx5jUadYIRtPtaq7AAAAwpXPpDWKDcdhiWr0tVDjsgW%2B4awGOM9VQ%3XPoMFuWcHsE42TK`,
},
goLiveMessage: {
fieldName: 'goLiveMessage',
label: 'Go Live Text',
maxLength: 200,
tip: 'The text to send when you go live.',
placeholder: `I've gone live! Come watch!`,
},
};

9
web/utils/server-status-context.tsx

@ -60,15 +60,6 @@ export const initialServerConfigState: ConfigDetails = { @@ -60,15 +60,6 @@ export const initialServerConfigState: ConfigDetails = {
notifications: {
browser: { enabled: false, goLiveMessage: '' },
discord: { enabled: false, webhook: '', goLiveMessage: '' },
twitter: {
enabled: false,
goLiveMessage: '',
apiKey: '',
apiSecret: '',
accessToken: '',
accessTokenSecret: '',
bearerToken: '',
},
},
externalActions: [],
supportedCodecs: [],

Loading…
Cancel
Save