Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposes alternative tipping logic #102

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 97 additions & 79 deletions scripts/brave_rewards/publisher/twitter/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,101 +2,119 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

import { getPort } from '../common/messaging'
import { getAuthHeaders, hasRequiredAuthHeaders } from './auth'

import * as types from './types'

let lastRequestTime = 0

const sendAPIRequest = (name: string, url: string) => {
return new Promise((resolve, reject) => {
if (!name || !url) {
reject(new Error('Invalid parameters'))
return
}

if (!hasRequiredAuthHeaders()) {
reject(new Error('Missing auth headers'))
return
}
let cachedXStore: any = null

const port = getPort()
if (!port) {
reject(new Error('Invalid port'))
return
}
const getXState = () => {
if (cachedXStore) {
return cachedXStore.getState()
}

if ((lastRequestTime !== 0) && (Date.now() - lastRequestTime < 3000)) {
reject(new Error('Ignoring API request due to network throttle'))
return
}
const hostNode = document.querySelector('#react-root > div > div')
const descriptors = Object.getOwnPropertyDescriptors(hostNode || {})

lastRequestTime = Date.now()

const authHeaders = getAuthHeaders()
port.postMessage({
type: 'OnAPIRequest',
mediaType: types.mediaType,
data: {
name,
url,
init: {
credentials: 'include',
headers: {
...authHeaders
},
referrerPolicy: 'no-referrer-when-downgrade',
method: 'GET',
redirect: 'follow'
if (hostNode) {
for (const propertyName in descriptors) {
if (propertyName.startsWith('__reactProps$')) {
const reactProps = hostNode[propertyName]
const store = reactProps?.children?.props?.store
if (store && typeof store.getState === 'function') {
cachedXStore = store
return cachedXStore.getState()
}
}
})
}
}

port.onMessage.addListener(function onMessageListener (msg: any) {
if (!port) {
reject(new Error('Invalid port'))
return
}
if (!msg || !msg.data) {
port.onMessage.removeListener(onMessageListener)
reject(new Error('Invalid message'))
return
}
if (msg.type === 'OnAPIResponse') {
if (!msg.data.name || (!msg.data.response && !msg.data.error)) {
port.onMessage.removeListener(onMessageListener)
reject(new Error('Invalid message'))
return
}
if (msg.data.name === name) {
port.onMessage.removeListener(onMessageListener)
if (msg.data.error) {
reject(new Error(msg.data.error))
return
}
resolve(msg.data.response)
}
}
})
})
throw new Error('XStore initialization failed')
}

export const getTweetDetails = async (tweetId: string) => {
const getEntities = () => getXState().entities

export const getTweetDetails = async (tweetId: string): Promise<types.TweetDetails> => {
if (!tweetId) {
return Promise.reject(new Error('Invalid parameters'))
throw new Error('Invalid parameters')
}

const entities = getEntities()
const tweet = entities.tweets.entities[tweetId]
jonathansampson marked this conversation as resolved.
Show resolved Hide resolved
const tweetUser = entities.users.entities[tweet?.user ?? '']

let response: types.TweetDetails = {}

/**
* We're explicitly checking that these properties both
* exist, and have the expected type before adding them
* to our response object.
*/

if (typeof tweet.created_at === 'string') {
response.created_at = tweet.created_at
}

if (typeof tweet.text === 'string') {
response.text = tweet.text
}

response.user = {}

if (typeof tweetUser.id_str === 'string') {
response.user.id_str = tweetUser.id_str
}

if (typeof tweetUser.screen_name === 'string') {
response.user.screen_name = tweetUser.screen_name
}

const url = `https://api.twitter.com/1.1/statuses/show.json?id=${tweetId}`
return sendAPIRequest('GetTweetDetails', url)
if (typeof tweetUser.name === 'string') {
response.user.name = tweetUser.name
}

if (typeof tweetUser.profile_image_url_https === 'string') {
response.user.profile_image_url_https = tweetUser.profile_image_url_https
}

return response
}

export const getUserDetails = async (screenName: string) => {
export const getUserDetails = async (screenName: string): Promise<types.UserDetails> => {
if (!screenName) {
return Promise.reject(new Error('Invalid parameters'))
throw new Error('Invalid parameters')
}

let user: types.UserEntity | null = null
const users = getEntities().users.entities
const userEntities: types.UserEntity[] = Object.values(users)

for (const value of userEntities) {
const nameLowered = value.screen_name.toLowerCase()
const screenNameLowered = screenName.toLowerCase()
if (nameLowered === screenNameLowered) {
user = value
break
}
}

if (!user) {
throw new Error('User not found')
}

let response: types.UserDetails = {}

/**
* We're explicitly checking that these properties both
* exist, and have the expected type before adding them
* to our response object.
*/

if (typeof user.id_str === 'string') {
response.id_str = user.id_str
}

if (typeof user.profile_image_url_https === 'string') {
response.profile_image_url_https = user.profile_image_url_https
}

const url =
`https://api.twitter.com/1.1/users/show.json?screen_name=${screenName}`
return sendAPIRequest('GetUserDetails', url)
return response
}
26 changes: 26 additions & 0 deletions scripts/brave_rewards/publisher/twitter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,29 @@ export const mediaDomain = 'twitter.com'

export const sendHeadersUrls = ['https://api.twitter.com/1.1/*']
export const sendHeadersExtra = ['requestHeaders', 'extraHeaders']

export type TweetDetails = {
user?: {
id_str?: string
screen_name?: string
name?: string
profile_image_url_https?: string
}
created_at?: string
text?: string
}

export type UserDetails = {
id_str?: string
profile_image_url_https?: string
}

export type UserEntity = {
screen_name: string
id_str: string
profile_image_url_https: string
}

export type UserEntities = {
[key: string]: UserEntity
}