Give a like automatically to anyone who comments your Instagram post with NestJS and "instagram-private-api"
Table of contents
No headings in the article.
Recently I wanted to start experimenting by adding some automatization to my own Instagram account. So I started this journey by building a simple backend project that will monitor my most recent Instagram post and give a heart to anyone who comments.
The first step was searching for the ability to access Instagram information programmatically, for this there's an official Instagram API, but to use it, there's quite a lot of steps. I attempted to create my app following all steps but ended up stuck in the process.
Luckily, I found instagram-private-api an NPM package that allows you to interact with Instagram, and it's also available for Python instagram_private_api
Before jumping into the code, let's plan how this should work, this process should be pretty similar to what we would do if we wanted to give a like to all post comments manually, the steps are:
- Login into our Instagram account
- Go to your profile
- Open your latest post
- Get the Instagram comments from those posts
- Filter to get comments without a like
- Give a like to those comments
So let the coding begin.
I started by creating a new NestJS project, adding a new module and service to place the code, and installing the instagram-private-api npm package.
nest new
nest g module comment-like
nest g service comment-like
npm i --save instagram-private-api
The first step should be to log into our Instagram account, Instagram-private-api exposes a method to log in that needs your username and password as your input. Because those credentials are pretty confidential information, we will store them inside environmental variables. So let's follow NestJS's Configuration technique
We need to install the package
npm i --save @nestjs/config
Also add the ConfigModule
as well as CommentLikeModule
to the imports
section inside app.module.ts
, below there's an example of how it should look like
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CommentLikeModule } from './comment-like/comment-like.module';
@Module({
imports: [
CommentLikeModule,
ConfigModule.forRoot(),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
Now let's make sure we can use the ConfigService, by adding the ConfigModule
into our imports
section inside comment-like.module.ts
, and also let's make sure we can export CommentLikeService
by adding it to our exports
section.
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { CommentLikeService } from './comment-like.service';
@Module({
imports:[ConfigModule],
providers: [CommentLikeService],
exports: [CommentLikeService],
})
export class CommentLikeModule {}
After finishing the ConfigModule setup, we can create a .env
file at the root folder where you'll need to place your account credentials
IG_USERNAME=your-username-here
IG_PASSWORD=your-password-here
Remember to place the file at root project
level
Now let's move to CommentLikeService
and we will place all the logic needed to do the login
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { IgApiClient } from 'instagram-private-api';
@Injectable()
export class CommentLikeService {
private logger = new Logger(CommentLikeService.name);
constructor(
private readonly configService: ConfigService,
) { }
async monitorComments() {
/// Read env instagram credentials
const username = this.configService.get('IG_USERNAME');
const password = this.configService.get('IG_PASSWORD');
/// Generate [IgApiClient]
const client = new IgApiClient();
client.state.generateDevice(username);
/// Login into Instagram with given credentials
await client.simulate.preLoginFlow();
const user = await client.account.login(username, password);
this.logger.log(`Logged in as ${user.username}`);
return `Welcome ${user.username} - ${user.full_name}!`;
}
}
The core logic lives into the monitorComments()
function, where we:
- Gather the Instagram credentials from the
.env
variables usingConfigService
. - Generate a new
IgApiClient
and generate a device uid. - Emulating a
preLoginFlow
and logging in using our credentials.
Now to be able to test this logic, let's move into our app.controller.ts
and update it as follows
import { Controller, Get } from '@nestjs/common';
import { CommentLikeService } from './comment-like/comment-like.service';
@Controller()
export class AppController {
constructor(
private readonly commentLikeService: CommentLikeService,
) {}
@Get('check-comment-likes')
async checkLikes() {
return this.commentLikeService.monitorComments();
}
}
Now you can test the function by accessing localhost:3000/check-comment-likes
You should see an image as follows, showing your Instagram username and full name.
So until now, we have completed the first step! Accessing our Instagram account!
Now let's move into seeing our own's account profile and accessing our last post.
/// Get my account's feed
const userFeed = client.feed.user(user.pk);
const recentPosts = await userFeed.items();
if (recentPosts.length === 0) {
return `${user.username}, you don't have any recent post ๐ญ`;
}
/// Get my latest post
const latestPost = recentPosts[0];
if (latestPost.comment_count === 0) {
return `${user.username}, your last post does not have any comment ๐คก`;
}
Here we just created a UserFeed
instance, that can let us access all the posts in our feed, for our use case we will just need our latest post, which will be the first element on the recentPosts
array, additionally, we will stop execution in two cases:
- If our account doesn't have any post
- If our latest post doesn't have any comment
Now that we have our last post identified, let's check all its comments and start giving those likes.
/// Gather post comments
const latestPostCommentFeed = client.feed.mediaComments(latestPost.pk);
/// Filter comments that need a like
const postComments = await latestPostCommentFeed.items();
const missingLikeComments = postComments.filter((comment) => {
return !comment.has_liked_comment && comment.user_id !== user.pk;
});
/// Give love to everyone
for await (const comment of missingLikeComments) {
await client.media.likeComment(comment.pk);
this.logger.log(`Liked ${comment.user.username} comment ๐: ${comment.text}`);
}
Here we created a MediaCommentsFeed
instance that lets us access all the comments inside a specific post, so we collect those posts awaiting their items()
method, then filter those comments so we ignore comments that already have our like, or comments that were made by us. Finally, we loop over those comments and assign the like using the likeComment()
function that the IgApiClient
exposes.
However, there's one thing we're missing! Pagination!
All instagram-private-api
feeds exposes data paginated, meaning that to fetch more comments you would need to call the items()
method again, you also can check if there are more items available by calling isMoreAvailable()
.
In our code, we store those feeds into userFeed
and latestPostCommentFeed
, we can skip pagination for userFeed
because we just need our latest posts, so now let's update the logic to use pagination and have access to all comments!
/// Gather post comments
const latestPostCommentFeed = client.feed.mediaComments(latestPost.pk);
let hasMoreComments = true;
while (hasMoreComments) {
/// Filter comments that need a like
const postComments = await latestPostCommentFeed.items();
const missingLikeComments = postComments.filter((comment) => {
return !comment.has_liked_comment && comment.user_id !== user.pk;
});
/// Give love to everyone
for await (const comment of missingLikeComments) {
await client.media.likeComment(comment.pk);
this.logger.log(`Liked ${comment.user.username} comment ๐: ${comment.text}`);
}
/// Check if there's more available
hasMoreComments = latestPostCommentFeed.isMoreAvailable();
}
So we wrap up the execution into a while
loop, that will keep running until there are no more posts available.
If you test it now by going to localhost:3000/check-comment-likes, you should see all your comments being automatically liked!
Now the cherry on top of the ice cream would be to make sure this logic runs frequently, we will do that by following the Task Scheduling technique from NestJS docs!
So let's begin by installing their package
npm install --save @nestjs/schedule
npm install --save-dev @types/cron
Then make sure to add the ScheduleModule
into the imports
inside app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CommentLikeModule } from './comment-like/comment-like.module';
@Module({
imports: [
CommentLikeModule,
ScheduleModule.forRoot(),
ConfigModule.forRoot(),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
After this is done, we just need to go to CommentLikeService
and add the @Cron()
decorator over the monitorComments()
function.
# Imports needed
import { Cron, CronExpression } from '@nestjs/schedule';
# Decorator
@Cron(CronExpression.EVERY_5_MINUTES)
async monitorComments() {
...
The @Cron()
decorator also accept any cron pattern as a string, you can use the presets from CronExpression
or build your own, for that I suggest you to use CronTabGuru
And that's it! You've made a script that will automatically like all comments from your latest instagram post!
All the code used on this project is available on this GitHub repository.