Hi Community,
I need to make a POST request by pure javascript without any modules(ex. twitter-API-v2 etc)
Here is my code.

const CryptoJS = require("crypto-js");
function getRandomString(length) {
    var randomChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var result = '';
    for ( var i = 0; i < length; i++ ) {
        result += randomChars.charAt(Math.floor(Math.random() * randomChars.length));
    }
    return result;
}
function hexToBase64(str) {
  return btoa(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));
}
async function postTweet() {

  const oauth_consumer_key = '<APP_CONSUMER_KEY>'
  const oauth_consumer_secret = '<APP_CONSUMER_SECRET>'
  const oauth_token = '<APP_TOKEN>'
  const oauth_token_secret = '<APP_TOKEN_SECRET>'
  const oauth_signature_method = 'HMAC-SHA1'
  const oauth_version = '1.0'
  const oauth_nonce = getRandomString(42)
  const oauth_timestamp = Math.round(Date.now() / 1000)

  const endpointURL = 'https://api.twitter.com/2/tweets';
  
  const tweetData = {
    status: 'The new NFT asset was uploaded to the blockchain'
  }
  
  const encodedTweet = encodeURIComponent(tweetData.status).replace(/!/g, '%21')

  const params = 'POST&' + encodeURIComponent('https://api.twitter.com/1.1/statuses/update.json') + '&include_entities' + encodeURIComponent('=true&') + 'oauth_consumer_key' + encodeURIComponent('='+oauth_consumer_key+'&') + 'oauth_nonce' + encodeURIComponent('='+oauth_nonce+'&') + 'oauth_signature_method' + encodeURIComponent('='+oauth_signature_method+'&') + 'oauth_timestamp' + encodeURIComponent('='+oauth_timestamp+'&') + 'oauth_token' + encodeURIComponent('='+oauth_token+'&') + 'oauth_version' + encodeURIComponent('='+oauth_version+'&') + 'status' + encodeURIComponent('='+encodedTweet)
  const signingKey = encodeURIComponent(oauth_consumer_secret) + '&' + encodeURIComponent(oauth_token_secret)
  //////HMAC-SHA1 Functionality//////
  const hexStr = CryptoJS.HmacSHA1(params, signingKey)
  console.log(hexStr)
  const signature = hexToBase64(hexStr)
  const oauth_signature = encodeURIComponent(signature)
  fetch('https://api.twitter.com/1.1/statuses/update.json', {
    method: 'post',
    mode: 'no-cors',
    headers: {
      'Authorization': 'OAuth oauth_consumer_key="'+oauth_consumer_key+'",oauth_token="'+oauth_token+'",oauth_signature_method="HMAC-SHA1",oauth_timestamp="'+oauth_timestamp+'",oauth_nonce="'+oauth_nonce+'",oauth_version="1.0",oauth_signature="'+oauth_signature+'"',
      'Content-Type': 'application/x-www-form-urlencoded' // 'application/json'
    },
    body: JSON.stringify(tweetData)
  }).then(function(response) {
    return response.json();
  }).then(function(data) {
    console.log('result:', data.text);
  });
}

But I keep 401 Unauthorized error.
Could you tell me what is wrong in my code?
Thanks in advance

1 Like

Why this restriction? This makes things significantly more error prone.

I think your parameters are not sorted: Authorizing a request | Docs | Twitter Developer Platform

Hi @IgorBrigadir Thanks for your response

This is because I need to use the Twitter API inside the Moralis Cloud Function. The Moralis Cloud does not support any modules.

2 Likes

Hey, do you mind sharing how you did the “request_token” part in javascript? I am having some trouble with it… Thanks

I am trying to do literally exactly the same thing! I’m using Cloudflare instead of Moralis but it’s the same problem. Cloudflare doesn’t support nodejs modules. Can you please show me how to do this if you figured it out?

I am making a Cloudflare Worker that Tweets from my Twitter Developer APIs whenever I make a blank GET request to the worker. This is basically the same thing you’re trying to do, because Cloudflare also doesn’t allow npm modules. So I attempted to do this with the following code.

index.ts

import { handleRequest } from './handler'

addEventListener('fetch', (event) => {
  event.respondWith(handleRequest(event.request))
})

handler.ts

import * as CryptoJS from 'crypto-js'

function getRandomString(length) {
    const randomChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let result = '';
    for ( let i = 0; i < length; i++ ) {
        result += randomChars.charAt(Math.floor(Math.random() * randomChars.length));
    }
    return result;
}
function hexToBase64(str) {
  const stringChange = str.toString()
  return btoa(String.fromCharCode.apply(null, stringChange.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));
}
async function postTweet() {

  const oauth_consumer_key = '<OAUTH CONSUMER KEY HERE>'
  const oauth_consumer_secret = '<OAUTH CONSUMER SECRET HERE>'
  const oauth_token = '<OAUTH TOKEN HERE>'
  const oauth_token_secret = '<OAUTH TOKEN SECRET HERE>'
  const oauth_signature_method = 'HMAC-SHA1'
  const oauth_version = '1.0'
  const oauth_nonce = getRandomString(42)
  const oauth_timestamp = Math.round(Date.now() / 1000)

  const endpointURL = 'https://api.twitter.com/1.1/statuses/update.json?status';
  
  const tweetData = {
    status: 'I am Tweeting this now'
  }
  
  const encodedTweet = encodeURIComponent(tweetData.status).replace(/!/g, '%21')

  const params = 'POST&' + encodeURIComponent('https://api.twitter.com/1.1/statuses/update.json') + '&include_entities' + encodeURIComponent('=true&') + 'oauth_consumer_key' + encodeURIComponent('='+oauth_consumer_key+'&') + 'oauth_nonce' + encodeURIComponent('='+oauth_nonce+'&') + 'oauth_signature_method' + encodeURIComponent('='+oauth_signature_method+'&') + 'oauth_timestamp' + encodeURIComponent('='+oauth_timestamp+'&') + 'oauth_token' + encodeURIComponent('='+oauth_token+'&') + 'oauth_version' + encodeURIComponent('='+oauth_version+'&') + 'status' + encodeURIComponent('='+encodedTweet)
  const signingKey = encodeURIComponent(oauth_consumer_secret) + '&' + encodeURIComponent(oauth_token_secret)
  //////HMAC-SHA1 Functionality//////
  const hexStr = CryptoJS.HmacSHA1(params, signingKey)
  console.log(hexStr)
  const signature = hexToBase64(hexStr)
  const oauth_signature = encodeURIComponent(signature)
  fetch('https://api.twitter.com/1.1/statuses/update.json', {
    method: 'post',
    headers: {
      'Authorization': 'OAuth oauth_consumer_key="'+oauth_consumer_key+'",oauth_token="'+oauth_token+'",oauth_signature_method="HMAC-SHA1",oauth_timestamp="'+oauth_timestamp+'",oauth_nonce="'+oauth_nonce+'",oauth_version="1.0",oauth_signature="'+oauth_signature+'"',
      'Content-Type': 'application/x-www-form-urlencoded' // 'application/json'
    },
    body: JSON.stringify(tweetData)
  }).then(function(response) {
    return response.json();
  }).then(function(data) {
    console.log('result:', data);
  });
  console.log('postTweet ran')
}

export async function handleRequest(request: Request): Promise<Response> {
  await postTweet()
  return new Response(`Hello worker! this is a ${request.method} request`, {
    headers: { 'content-type': 'text/plain' },
  });
}

But when I run the code with wrangler dev and then do a blank GET request to http://127.0.0.1:8787 with Postman, I get this in my terminal:

<myusername>@<mycomputername> <myappname> % wrangler dev
👂  Listening on http://127.0.0.1:8787
🌀  Detected changes...
💁  Ignoring stale first change
[2022-04-24 15:42:37] GET <myappname>.<myworkername>.workers.dev/ HTTP/1.1 200 OK
{unknown object}
postTweet ran
^C
<myusername>@<mycomputername> <myappname> %

I noticed that the problem probably starts with the fact that const hexStr = CryptoJS.HmacSHA1(params, signingKey) is failing. You can see in the output that console.log(hexStr) is printing {unknown object}

What am I doing wrong? And how can I get my Cloudflare worker to Tweet upon request?