Skip to content

Commit

Permalink
feat: Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr-Destructive committed Oct 31, 2021
0 parents commit 305f58a
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 0 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Releases
on:
push:
branches:
- main

jobs:
changelog:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: conventional Changelog Action
id: changelog
uses: TriPSs/conventional-changelog-action@v3.7.1
with:
github-token: ${{ secrets.CHANGELOG_RELEASE }}

- name: create release
uses: actions/create-release@v1
if: ${{ steps.changelog.outputs.skipped == 'false' }}
env:
GITHUB_TOKEN: ${{ secrets.CHANGELOG_RELEASE }}
with:
tag_name: ${{ steps.changelog.outputs.tag }}
release_name: ${{ steps.changelog.outputs.tag }}
body: ${{ steps.changelog.outputs.clean_changelog }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
keys.txt
config.txt
temp.json
89 changes: 89 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
## Crossposter

> Crosspost your articles to dev.to/medium.com/hashnode.com from the command line
Crosspost.sh is a shellscript(BASH) to automate crossposting to platforms like dev.to, medium.com and hashnode.com. The script takes in markdown version of your post with a few inputs from you and posts it to those platforms. You would require a token/key for each of those platforms to post it from the command line.

The actual script is still not perfect (has a few bugs). Though it posts on `dev.to` and `medium.com` easily, the `hashnode.com` is buggy as it parses the raw markdown into the post and doesn't render as desired. So, **its a under-development script**, fell free to raise any issues or PRs on the official GitHub repo.

Run the script on a bash interpreter with the command:

`bash crosspost.sh`

For posting the article you need to provide the following details:

## Front-Matter

### Meta data about the post

- Title of Post
- Subtitle of Post
- Publish status of post(`true` or `false`)
- Tags for the post (comma separated values)
- Canonical Url (original url of the post)
- Cover Image (URL of the post's image/thumbnail)

This information is a must for `dev.to` especially the `title`. This should be provide in the same order as given below:

```
---
title: The title of the post
subtitle: The description of your article
published: true
tags: programming, anythingelse
canonical url: url of your original blog
cover_image: coverimage_url
---
```

There is no need to enclose any of them with quotation marks. `Published` argument will be `true` if you want to publish it and `false` if you want to keep it in your Drafts.

In the demonstrations, we just need to enter the tokens once. The tokens will be stored locally in the `keys.txt` file and retrieved later within the script.

### Posting on **dev.to**:

Posting on dev.to requires their `API key` which can be generated by going on the [Dev Community API Keys](https://dev.to/settings/account/). From there you can generate a new key with any name you like. You just need to enter the key to CLI once or manually enter in the `keys.txt` file with the format `dev.to:key` on the first line. This will be used for the future cross-posting whenever you execute the shell script(`bash crosspost.sh`)

You can provide the [front matter](#front-matter) manually in your markdown file or you will be prompted for the input. So, that is all you will require for posting on dev.to from the Command line.

Lets see the script in action

![dev.to](https://gitlab.com/MR_DESTRUCTIVE/tblog-img/-/raw/main/devto.gif)

If you want to add in more stuff to the post, you can check out the [DEV.to API docs](https://developers.forem.com/api#operation/createArticle) which is powered by [Forem](https://www.forem.com/), there a ton of options you can hook to the front-matter in the shellscript.

**NOTE: There is a limit of 10 requests per 30 seconds, so keep in mind while testing the script and don't try to spam**

### Posting on **hashnode.com**:

This part is still under development as it only displays the raw markdown in the post, also the `tags` are too heavy to implement from the API as `id` of every tag is required along with the `slug` and `name`. Still it serves some purpose at least. For posting on `hashnode.com`, we need `Personal Access Token`. This can be generated by going to [Developer Settings](https://hashnode.com/settings/developer). You will also require the user-id of your `hashnode` account. You can get your user-id/username from the [settings](https://hashnode.com/settings) tab under profile information. We require Username for posting to the Publication Blog if any. As usual, the `Personal Access Token` for interacting with the [Hashnodes' GraphQL API](https://api.hashnode.com/). The API is quite user friendly and provides everything in one place. There are docs for running each and every `query` and `mutations` present in the API.

You can paste the token when prompted from the script or manually type in the `keys.txt` text file as `hashnode:token` on the 4th line. Yes, that should be on the `4th` line, thats make retrieving much more easier and safe. Next also input in the `username` when the script asks for the input or again type in on the `5th` line, `hashnode_id:username` in the text file `keys.txt`. Please enter the credentials from the script prompt so as to avoid errors and misconfigurations when doing manually

This will create the Post on hashnode with the title, subtitle, cover image correctly but will mess up the content. I tried hard but its just not happening. There needs to be some character for newline as the API rejects the `rn` characters passed in, so I have substited them with `br` and the result is raw markdown. **As the Hashnode API is still under development and they are bringing changes and new features in, the API should improve in its core functionality and make it much easier for creating some common queries**. So, I'll create issue on GitHub for posting the actual content via the script.

So, this is the demonstration of posting on hashnode.

[hashnode](https://gitlab.com/MR_DESTRUCTIVE/tblog-img/-/raw/main/hashnode.gif)

### Posting on **medium.com**:

Medium API is much more versatile and markdown friendly, though it has some limitation on the number of posts you can make in a day. For posting on `Medium.com`, we will require the `Integration Token` which can be generated on the [settings tab](https://medium.com/me/settings). As similar to `hashnode`, you can name the token whatever you like and then get the token. Paste the token when prompted from the script or manually type in the `keys.txt` text file as `medium:token` on the `2nd` line. We also require the Medium_id, but we can get that from the token itself, so inside the script once the token is obtained, the curl command is executed to fetch in the `id` and it is stored on the next(`3rd`) line in the `keys.txt` file for actually posting on `medium.com`. So that is all the configuration you need for posting on `medium.com`.

There is some documentation on [Medium API](https://github.com/Medium/medium-api-docs), we can even post to a Publication, that shall be created in future. Also the cover images can be posted on medium, it is not currently done but that can again be a #TODO. **The tags are not rendered on Medium yet with the script.** The way we can parse strings is limited in BASH, so this might still be a doable thing later. Most of the checkboxes are ticked like title, subtitle, cover-image, canonical url, and importantly the content.

Let's look at post on medium from the script.

[medium](https://gitlab.com/MR_DESTRUCTIVE/tblog-img/-/raw/main/medium.gif)

### All platforms:

Now, once you have configured every thing, you can opt for the `4` choice that is post on all platforms(dev.to, hashnode and medium), but as hashnode is not looking a good option right now, so there is the `5` option for only `dev.to` and `medium`.

[all-platforms](https://gitlab.com/MR_DESTRUCTIVE/tblog-img/-/raw/main/crossposter.gif)

## Contributions

This is not a perfect script and has a lot of bugs due to encoding the markdown in BASH. It is subject to improve over time as I get to understand more string manipulation in BASH and render the post as expected. Please feel free to open up a genuine and smallest of issues.


178 changes: 178 additions & 0 deletions crosspost.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#!/usr/bin/env bash

function dev()
{
if [[ -z $(sed -n -e 's/.*dev.to://p' $keys) ]];then
read -p "Enter the dev.to API key : " dev_key
sed -i "/dev.to:/ s/$/$dev_key/" $keys
fi
curl -s -X POST -H "Content-Type: application/json" \
-H "api-key: $dev_key" -d "{\"article\":{\"body_markdown\":\"$body\"}}" \
https://dev.to/api/articles

if [[ $? ]];then
echo -e "\nPosted on dev.to\n"
else
echo -e "Error...\nFailed to post on dev.to"
fi
}

function hashnode()
{
if [[ -z $(sed -n -e 's/.*hashnode://p' $keys ) ]]; then
read -p "Enter the hashnode.com token : " hashtoken
sed -i "/hashnode:/ s/$/$hashtoken/" $keys
fi

if [[ -z $(sed -n -e 's/.*node_id://p' $keys ) ]]; then
read -p "Enter the hashnode username : " hash_uname
curl -s 'https://api.hashnode.com/' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Connection: keep-alive' -H 'DNT: 1' -H 'Origin: https://api.hashnode.com' -H 'Authorization: \"'"$hash_token"'\"' --data-binary '{"query":"query user {\n user(username: \"'"$hash_uname"'\") {\n publication {\n _id\n }\n }\n}\n"}' --compressed >temp.json
hash_id=$(grep -o -P '(?<=id":").*(?=")' temp.json)
sed -i "/hashnode_id:/ s/$/$hash_id/" $keys
fi



awk '{print $0"\\r\\n"}' temp.txt >temp.md
cat temp.md | tr -d '\r\n' | sed 's/\\r\\n/<br>/g' >body.md
body=$(cat body.md)

# comment out the below two lines for non-publication posts
#curl 'https://api.hashnode.com/' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: application/json'\
# -H 'Connection: keep-alive' -H 'DNT: 1' -H 'Origin: https://api.hashnode.com' -H 'Authorization: '$hash_token'' --data-binary '{"query":"mutation {\n createStory(\n input: {\n title: \"'"$title"'\",\n contentMarkdown: \"'"$body"'\"\n tags: [\n {\n _id: \"56744721958ef13879b94ff1\",\n name: \"General Programming\",\n slug: \"programming\"\n }\n ]\n }\n ) {\n message\n post{\n title\n }\n }\n}\n\n "}' --compressed

curl -s 'https://api.hashnode.com/' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: application/json'\
-H 'Connection: keep-alive' -H 'DNT: 1' -H 'Origin: https://api.hashnode.com' -H 'Authorization: '$hashtoken'' --data-binary '{"query":"mutation {\n createPublicationStory(\n input: {\n title: \"'"$title"'\",\n contentMarkdown: \"'"$body"'\"\n tags: [\n {\n _id: \"56744721958ef13879b94ffc\",\n name: \"General Programming\",\n slug: \"programming\"\n }\n ]\n coverImageURL:\"'"$cover_image"'\" }\n publicationId:\"'"$hash_id"'\",\n hideFromHashnodeFeed:false\n ) {\n message\n post{\n title\ncoverImage\n }\n }\n}\n"}' --compressed

if [[ $? ]];then
echo -e "\nPosted on hashnode.com\n"
else
echo -e "Error...\nFailed to post on hashnode.com"
fi
}

function medium()
{
if [[ -z $(sed -n -e 's/.*ium://p' $keys ) ]]; then
read -p "Enter the medium.com API token : " med_token
med_id=$(curl -s -H "Authorization: Bearer $med_token" https://api.medium.com/v1/me | json_pp >temp.json)
med_id=$(grep -o -P '(?<=id).*(?=,)' temp.json )
med_id=$(echo ${med_id:3} | tr -d '"')
sed -i "/medium:/ s/$/$med_token/" $keys
sed -i "/medium_id:/ s/$/$med_id/" $keys
fi

tags=$(echo $tags | sed "s/ //g")
tags=$(echo $tags | sed "s/,/','/g" | sed "s/$/'/" | sed "s/^/'/" | sed "s/\b\(.\)/\u\1/g")
if [[ $statusp = "true" ]];then
mstatus="public"
else
mstatus="draft"
fi
sed -i "1a # $title" temp.txt
sed -i "1a ![]($cover_image)" temp.txt
sed -i "2a ## $subtitle" temp.txt


awk '{print $0"\\r\\n"}' temp.txt >temp.md
cat temp.md | tr -d '\r\n' > body.md
body=$(cat body.md)

curl -s -H "Authorization: Bearer $med_token " -H "Content-Type: application/json" \
-H "Accept: application/json" -H "Accept-Charset: utf-8" --request "POST" \
-d '{ "title": "'"$title"'", "contentFormat": "markdown", "content": "'"$body"'", "canonicalUrl": "'"$canonical_url"'", "tags": ["'"$tags"'"],"publishStatus": "'"$mstatus"'" }'\
"https://api.medium.com/v1/users/$med_id/posts"

if [[ $? ]];then
echo -e "\nPosted on medium.com\n"
else
echo -e "Error...\nFailed to post on medium.com"
fi
}


touch keys.txt body.md temp.md temp.txt
keys=keys.txt

if [[ ! -s $keys ]];then
head -n 1 $keys | echo "dev.to:" >$keys
sed -i "1a medium:" $keys
sed -i "2a medium_id:" $keys
sed -i "3a hashnode:" $keys
sed -i "4a hashnode_id:" $keys
else
dev_key=$(sed -n -e 's/dev.to://p' $keys)
med_token=$(sed -n -e 's/medium://p' $keys)
med_id=$(sed -n -e 's/medium_id://p' $keys)
hashtoken=$(sed -n -e 's/hashnode://p' $keys)
hash_id=$(sed -n -e 's/hashnode_id://p' $keys)
fi

read -p "Enter the name of the file : " file

if [[ -z $(sed '/^---/p;q' $file) ]];then
read -p "Enter the title of your post : " title
read -p "Enter the subtitle of your post : " subtitle
read -p "Enter the status of your post(true/false) : " statusp
read -p "Enter the tags for your post : " tags
read -p "Enter the canonical url of the post : " canonical_url
read -p "Enter the cover_image url of the post : " cover_image

sed -i "1i ---" $file
sed -i "1a title: $title" $file
sed -i "2a subtitle: $subtitle" $file
sed -i "3a published: $statusp" $file
sed -i "4a tags: $tags" $file
sed -i "5a canonical_url: $canonical_url" $file
sed -i "6a cover_image: $cover_image" $file
sed -i "7a ---" $file
fi

title=$(sed -n '2 s/^[^=]*title: *//p' $file)
subtitle=$(sed -n '3 s/^[^=]*subtitle: *//p' $file)
tags=$(sed -n '5 s/^[^=]*tags: *//p' $file)
canonical_url=$(sed -n '6 s/^[^=]*url: *//p' $file)
cover_image=$(sed -n '7 s/^[^=]*age: *//p' $file)

cat $file | sed '1,10{/^---/!{/^\---/!d}}' | sed '1,2d' >temp.txt
awk '{print $0"\\r\\n"}' $file >temp.md
cat temp.md | tr -d '\r\n' > body.md
body=$(cat body.md)


echo -e "\n1. dev.to \n"
echo -e "2. hashnode.com \n"
echo -e "3. medium.com \n"
echo -e "4. All of the above\n"
echo -e "5. dev.to and medium\n"

read -p "Where you want to cross post to? " num

if [[ $num -eq 1 ]];then

dev

elif [[ $num -eq 2 ]];then

hashnode

elif [[ $num -eq 3 ]];then

medium

elif [[ $num -eq 4 ]];then

dev
hashnode
medium

elif [[ $num -eq 5 ]];then

dev
medium

else
echo "Invalid Input"
fi

rm temp.md temp.txt body.md

0 comments on commit 305f58a

Please sign in to comment.