npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@codybrom/denim

v1.3.4

Published

Typescript/Deno module to simplify posting to Threads

Downloads

456

Readme

Denim

JSR JSR Score

Denim is a Deno module that provides a simple interface for posting single Threads posts using text, images, or videos.

Features

  • Create and publish posts on Threads with an easy-use-API
  • Supports text-only, image, video, and carousel posts
  • Add alt text to image and video posts
  • Attach links to text posts
  • Geo-gate content to specific countries
  • Control who can reply to posts
  • Retrieve publishing rate limit information
  • Ready to deploy as an edge function

Installation

Using with Deno

To add Denim to your Deno project, you can use the following command:

deno add @codybrom/denim

This will add the latest version of Denim to your project's dependencies.

Usage

To import straight from JSR:

import { ThreadsPostRequest, createThreadsContainer, publishThreadsContainer } from 'jsr:@codybrom/denim';

Basic Usage

import { createThreadsContainer, publishThreadsContainer, ThreadsPostRequest } from "jsr:@codybrom/denim";

const request: ThreadsPostRequest = {
  userId: "YOUR_USER_ID",
  accessToken: "YOUR_ACCESS_TOKEN",
  mediaType: "TEXT",
  text: "Check out Denim on GitHub!",
  linkAttachment: "https://github.com/codybrom/denim",
  replyControl: "everyone",
};

// Create a container
const containerId = await createThreadsContainer(request);

// Publish the container
const publishedId = await publishThreadsContainer(request.userId, request.accessToken, containerId);

console.log(`Post published with ID: ${publishedId}`);

Retrieving Publishing Rate Limit

import { getPublishingLimit } from "jsr:@codybrom/denim";

const userId = "YOUR_USER_ID";
const accessToken = "YOUR_ACCESS_TOKEN";

try {
  const rateLimit = await getPublishingLimit(userId, accessToken);
  console.log("Current usage:", rateLimit.quota_usage);
  console.log("Total quota:", rateLimit.config.quota_total);
  console.log("Quota duration (seconds):", rateLimit.config.quota_duration);
} catch (error) {
  console.error("Failed to retrieve rate limit information:", error);
}

Posting Different Media Types

Text-only Post

const textRequest: ThreadsPostRequest = {
  userId: "YOUR_USER_ID",
  accessToken: "YOUR_ACCESS_TOKEN",
  mediaType: "TEXT",
  text: "This is a text-only post on Threads!",
};

Text Post with Link Attachment

const textRequest: ThreadsPostRequest = {
  userId: "YOUR_USER_ID",
  accessToken: "YOUR_ACCESS_TOKEN",
  mediaType: "TEXT",
  text: "This is a post with an attached link on Threads!",
  linkAttachment: "https://example.com",
};

Image Post with Alt Text

const imageRequest: ThreadsPostRequest = {
  userId: "YOUR_USER_ID",
  accessToken: "YOUR_ACCESS_TOKEN",
  mediaType: "IMAGE",
  text: "Check out this image!",
  imageUrl: "https://example.com/image.jpg",
  altText: "A beautiful sunset over the ocean",
};

Video Post

const videoRequest: ThreadsPostRequest = {
  userId: "YOUR_USER_ID",
  accessToken: "YOUR_ACCESS_TOKEN",
  mediaType: "VIDEO",
  text: "Watch this video!",
  videoUrl: "https://example.com/video.mp4",
};

Video Post with Alt Text, Reply Control and Geo-gating* (requires special account permission)

const videoRequest: ThreadsPostRequest = {
  userId: "YOUR_USER_ID",
  accessToken: "YOUR_ACCESS_TOKEN",
  mediaType: "VIDEO",
  text: "Watch this video!",
  videoUrl: "https://example.com/video.mp4",
  altText: "A tutorial on how to make a chocolate cake",
  allowlistedCountryCodes: ["US", "GB"],
  replyControl: "mentioned_only",
};

Carousel Post

import { createCarouselItem, createThreadsContainer, publishThreadsContainer, ThreadsPostRequest } from "jsr:@codybrom/denim";

// First, create carousel items
const item1Id = await createCarouselItem({
  userId: "YOUR_USER_ID",
  accessToken: "YOUR_ACCESS_TOKEN",
  mediaType: "IMAGE",
  imageUrl: "https://example.com/image1.jpg",
  altText: "First image in the carousel",
});

const item2Id = await createCarouselItem({
  userId: "YOUR_USER_ID",
  accessToken: "YOUR_ACCESS_TOKEN",
  mediaType: "VIDEO",
  videoUrl: "https://example.com/video.mp4",
  altText: "Video in the carousel",
});

// Then, create the carousel post
const carouselRequest: ThreadsPostRequest = {
  userId: "YOUR_USER_ID",
  accessToken: "YOUR_ACCESS_TOKEN",
  mediaType: "CAROUSEL",
  text: "Check out this carousel post!",
  children: [item1Id, item2Id],
  replyControl: "everyone",
};

const containerId = await createThreadsContainer(carouselRequest);
const publishedId = await publishThreadsContainer(carouselRequest.userId, carouselRequest.accessToken, containerId);

console.log(`Carousel post published with ID: ${publishedId}`);

Deploying as an Edge Function

Denim can be easily deployed as an edge function. An example implementation is provided in examples/edge-function.ts.

To deploy:

  1. Copy the examples/edge-function.ts file to your project.
  2. Deploy this file to your serverless platform that supports Deno.
  3. Send POST requests to your function's URI with the appropriate JSON body.

Example cURL Commands

# Post a text-only Thread
curl -X POST <YOUR_FUNCTION_URI> \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_AUTH_KEY" \
  -d '{
    "userId": "YOUR_USER_ID",
    "accessToken": "YOUR_ACCESS_TOKEN",
    "mediaType": "TEXT",
    "text": "Hello from Denim!"
  }'

# Post an image Thread
curl -X POST <YOUR_FUNCTION_URI> \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_AUTH_KEY" \
  -d '{
    "userId": "YOUR_USER_ID",
    "accessToken": "YOUR_ACCESS_TOKEN",
    "mediaType": "IMAGE",
    "text": "Check out this image I posted with Denim!",
    "imageUrl": "https://example.com/image.jpg"
  }'

# Post a video Thread
curl -X POST <YOUR_FUNCTION_URI> \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_AUTH_KEY" \
  -d '{
    "userId": "YOUR_USER_ID",
    "accessToken": "YOUR_ACCESS_TOKEN",
    "mediaType": "VIDEO",
    "text": "Watch this video I posted with Denim!",
    "videoUrl": "https://example.com/video.mp4"
  }'

Note: Replace with your actual authorization headers if your edge function requires them (or remove them).

Security Note

Ensure that your function is deployed with appropriate access controls and authentication mechanisms to protect sensitive data like access tokens.

Testing

To run the tests:

deno test mod_test.ts

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License