Skip to content
Open
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
67 changes: 67 additions & 0 deletions NEURAL_NETWORK_POSTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Neural Network Page Posting Feature

This feature allows the VK bot to automatically post content to VK communities focused on neural networks, AI, and language models.

## Configuration

To use this feature, edit the `neural-network-communities.json` file:

```json
{
"communities": [
{
"id": 123456789,
"name": "AI Community",
"description": "Community about artificial intelligence",
"enabled": true
},
{
"id": 987654321,
"name": "Neural Networks Group",
"description": "Group for neural network enthusiasts",
"enabled": true
}
]
}
```

## How to find VK Community IDs

1. Go to the VK community page
2. Look at the URL: `https://vk.com/club123456789` or `https://vk.com/public123456789`
3. The number after `club` or `public` is the community ID

## Post Content

The bot will randomly select from AI/ML-focused messages that include:
- Information about neural networks and language models
- Links to GPT bot services
- Invitations for collaboration in AI field
- Contact information for discussions

## Posting Schedule

- Posts are sent every 15 minutes (configurable in `index.js`)
- The bot checks for existing posts to avoid duplicates
- Previous posts are automatically cleaned up

## Error Handling

The bot handles various VK API errors:
- Access denied (community walls disabled)
- Rate limiting (automatic delays)
- Captcha requirements
- Advertisement posting limits

Communities that encounter errors are temporarily disabled and retried after 24 hours.

## Testing

Run the tests with:
```bash
npm test __tests__/triggers/neural-network-page-posts.test.js
```

## Manual Configuration

If you need to add communities manually, edit the `neuralNetworkCommunities` array in `triggers/neural-network-page-posts.js`.
62 changes: 62 additions & 0 deletions __tests__/triggers/neural-network-page-posts.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const { trigger, neuralNetworkPostMessages } = require('../../triggers/neural-network-page-posts');

describe('neural network page posts trigger', () => {
const triggerDescription = 'neural network page posts trigger';

it('should have correct trigger structure', () => {
expect(trigger).toHaveProperty('name');
expect(trigger).toHaveProperty('action');
expect(trigger.name).toBe('SendNeuralNetworkPagePosts');
expect(typeof trigger.action).toBe('function');
});

it('should have neural network focused post messages', () => {
expect(neuralNetworkPostMessages).toBeDefined();
expect(Array.isArray(neuralNetworkPostMessages)).toBe(true);
expect(neuralNetworkPostMessages.length).toBeGreaterThan(0);

// Check that messages contain AI/ML related keywords
neuralNetworkPostMessages.forEach(message => {
expect(typeof message).toBe('string');
expect(message.length).toBeGreaterThan(0);

// Should contain at least one AI/ML related term
const aiTerms = ['Π½Π΅ΠΉΡ€ΠΎΠ½Π½', 'AI', 'machine learning', 'GPT', 'искусствСнного ΠΈΠ½Ρ‚Π΅Π»Π»Π΅ΠΊΡ‚Π°', 'neural'];
const containsAiTerms = aiTerms.some(term =>
message.toLowerCase().includes(term.toLowerCase())
);
expect(containsAiTerms).toBe(true);
});
});

it('should not execute when no communities are configured', async () => {
const mockContext = {
vk: {
api: {
wall: {
get: jest.fn(),
search: jest.fn(),
post: jest.fn(),
delete: jest.fn()
}
}
}
};

// Mock console.log to verify the skip message
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();

await trigger.action(mockContext);

expect(consoleSpy).toHaveBeenCalledWith(
trigger.name,
'No neural network communities configured. Skipping.'
);

// VK API should not be called
expect(mockContext.vk.api.wall.get).not.toHaveBeenCalled();
expect(mockContext.vk.api.wall.post).not.toHaveBeenCalled();

consoleSpy.mockRestore();
});
});
6 changes: 6 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,9 @@ const sendBirthDayCongratulationsIntervalAction = async () => {
}
const sendBirthDayCongratulationsInterval = setInterval(sendBirthDayCongratulationsIntervalAction, (23 * 60 * minute) / ms);
// sendBirthDayCongratulationsIntervalAction();

const { trigger: sendNeuralNetworkPostsTrigger } = require('./triggers/neural-network-page-posts');
const sendNeuralNetworkPostsIntervalAction = async () => {
await executeTrigger(sendNeuralNetworkPostsTrigger, { vk });
};
const sendNeuralNetworkPostsInterval = setInterval(sendNeuralNetworkPostsIntervalAction, (15 * minute) / ms);
22 changes: 22 additions & 0 deletions neural-network-communities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"communities": [
{
"id": null,
"name": "Example Neural Network Community",
"description": "Add your neural network/AI community IDs here. Find VK community ID from URL: vk.com/club[ID] or vk.com/public[ID]",
"enabled": false
}
],
"instructions": {
"how_to_add": "To add a neural network community, find the VK community ID and add it to the 'communities' array",
"community_id_format": "VK community ID should be a positive number (e.g., 123456789)",
"finding_community_id": "Look at the VK community URL: vk.com/club123456789 or vk.com/public123456789",
"example": {
"id": 123456789,
"name": "AI Community",
"description": "Community about artificial intelligence",
"enabled": true
},
"note": "The bot will only post to communities where it has posting permissions"
}
}
183 changes: 183 additions & 0 deletions triggers/neural-network-page-posts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
const { sleep, getRandomElement, second, minute, ms, day, app } = require('../utils');
const fs = require('fs').promises;
const path = require('path');

// Load neural network communities from configuration
async function loadNeuralNetworkCommunities() {
try {
const configPath = path.join(__dirname, '..', 'neural-network-communities.json');
const configData = await fs.readFile(configPath, 'utf8');
const config = JSON.parse(configData);

return config.communities
.filter(community => community.enabled && community.id)
.map(community => community.id);
} catch (error) {
console.warn('Failed to load neural network communities configuration:', error.message);
return [];
}
}

let neuralNetworkCommunities = [];

let disabledNeuralCommunities = [];

const disabledCommunitiesCleanupInterval = setInterval(() => {
if (app.gracefullyFinished) {
clearInterval(disabledCommunitiesCleanupInterval);
return;
}
disabledNeuralCommunities = [];
}, (1 * day) / ms);

// Neural network and AI-focused post content
const neuralNetworkPostMessages = [
`🧠 ΠŸΡ€ΠΈΠ²Π΅Ρ‚! Π― программист, Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰ΠΈΠΉ с Π½Π΅ΠΉΡ€ΠΎΠ½Π½Ρ‹ΠΌΠΈ сСтями ΠΈ языковыми модСлями.
ВсСгда Π³ΠΎΡ‚ΠΎΠ² ΠΎΠ±ΡΡƒΠ΄ΠΈΡ‚ΡŒ AI, machine learning, ΠΈ Π΄Π΅Π»ΠΈΡ‚ΡŒΡΡ ΠΎΠΏΡ‹Ρ‚ΠΎΠΌ Π² этой области.
ΠŸΡ€ΠΈΠ½ΠΈΠΌΠ°ΡŽ всС заявки Π² Π΄Ρ€ΡƒΠ·ΡŒΡ ΠΎΡ‚ Π΅Π΄ΠΈΠ½ΠΎΠΌΡ‹ΡˆΠ»Π΅Π½Π½ΠΈΠΊΠΎΠ²!

πŸ€– Для доступа ΠΊ GPT: https://t.me/DeepGPTBot?start=1339837872 (наша Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°)
πŸ“± Telegram: https://t.me/link_konard - ΠΊΠ°Π½Π°Π», https://t.me/drakonard - Π»ΠΈΡ‡ΠΊΠ°`,

`πŸš€ Π˜Π½Ρ‚Π΅Ρ€Π΅ΡΡƒΠ΅ΡˆΡŒΡΡ Π½Π΅ΠΉΡ€ΠΎΠ½Π½Ρ‹ΠΌΠΈ сСтями ΠΈ языковыми модСлями?
Π― программист, ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΡŽΡ‰ΠΈΠΉΡΡ Π½Π° AI/ML. Π”Π°Π²Π°ΠΉΡ‚Π΅ Π΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ ΠΈ ΠΎΠ±ΠΌΠ΅Π½ΠΈΠ²Π°Ρ‚ΡŒΡΡ знаниями!

πŸ’¬ Пиши Π² Π»ΠΈΡ‡ΠΊΡƒ - обсудим Π»ΡŽΠ±Ρ‹Π΅ вопросы ΠΏΠΎ ΠΌΠ°ΡˆΠΈΠ½Π½ΠΎΠΌΡƒ ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΡŽ
πŸ”— ΠŸΠΎΠ»Π΅Π·Π½Ρ‹Π΅ ссылки Π² ΠΌΠΎΠ΅ΠΌ ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅`,

`🎯 Π˜Ρ‰Ρƒ Π΅Π΄ΠΈΠ½ΠΎΠΌΡ‹ΡˆΠ»Π΅Π½Π½ΠΈΠΊΠΎΠ² Π² области искусствСнного ΠΈΠ½Ρ‚Π΅Π»Π»Π΅ΠΊΡ‚Π°!
ΠŸΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡΡ‚ с ΠΎΠΏΡ‹Ρ‚ΠΎΠΌ Π² neural networks. ΠŸΡ€ΠΈΠ½ΠΈΠΌΠ°ΡŽ заявки Π² Π΄Ρ€ΡƒΠ·ΡŒΡ.

πŸ“š Π“ΠΎΡ‚ΠΎΠ² ΠΏΠΎΠ΄Π΅Π»ΠΈΡ‚ΡŒΡΡ ΠΎΠΏΡ‹Ρ‚ΠΎΠΌ ΠΈ ΡƒΠ·Π½Π°Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π½ΠΎΠ²ΠΎΠ΅
πŸ’Ό ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ для интСрСсных ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ² ΠΈ ΠΊΠΎΠ»Π»Π°Π±ΠΎΡ€Π°Ρ†ΠΈΠΉ`
];

const postsSearchRequest = `Π½Π΅ΠΉΡ€ΠΎΠ½Π½`; // Part of "Π½Π΅ΠΉΡ€ΠΎΠ½Π½Ρ‹ΠΉ" to find existing posts

const avatarImagePath = 'avatar.jpeg';

/**
* Uploads an avatar image for neural network communities
*/
async function uploadAvatarPicture(context, communityId, imagePath) {
// Using the same avatar approach as the main posting trigger
return 'photo3972090_457245285_5f56ac9e1f0de697db';
}

function disableNeuralCommunity(communityId) {
if (!disabledNeuralCommunities.includes(communityId)) {
disabledNeuralCommunities.push(communityId);
console.warn(trigger.name, `Neural network community ${communityId} is added to disabled communities list.`);
}
}

async function sendNeuralNetworkPosts(context) {
try {
// Load communities from configuration each time to allow dynamic updates
neuralNetworkCommunities = await loadNeuralNetworkCommunities();

if (neuralNetworkCommunities.length === 0) {
console.log(trigger.name, 'No neural network communities configured. Skipping.');
return;
}

for (const communityId of neuralNetworkCommunities) {
if (disabledNeuralCommunities.includes(communityId)) {
console.log(trigger.name, `Neural network community ${communityId} is disabled. Skipping.`);
continue;
}

try {
// For wall posts, VK expects a negative owner_id for communities.
const ownerId = '-' + communityId.toString();

const topPosts = await context.vk.api.wall.get({
owner_id: ownerId,
count: 10
});

const topPostsHaveNeuralPost = topPosts.items.some(post =>
post.text.toLowerCase().includes(postsSearchRequest.toLowerCase())
);

console.log(trigger.name, `Loaded ${topPosts.items.length} posts from neural network community ${communityId}. Our neural network post is ${topPostsHaveNeuralPost ? 'found' : 'not found'} in these posts.`);

await sleep(trigger.name, (10 * second) / ms);

if (topPostsHaveNeuralPost) {
continue;
}

const previousPosts = await context.vk.api.wall.search({
owner_id: ownerId,
query: postsSearchRequest,
count: 15
});
const postsToDelete = previousPosts.items.filter(post =>
post.text.toLowerCase().includes(postsSearchRequest.toLowerCase()) && post.can_delete
);

console.log(trigger.name, `Found ${postsToDelete.length} previous neural network posts to be deleted.`);
await sleep(trigger.name, (5 * second) / ms);
console.log(trigger.name, `Sending neural network post to ${communityId} community.`);

const message = getRandomElement(neuralNetworkPostMessages);
const avatarAttachment = await uploadAvatarPicture(context, communityId, avatarImagePath);
const attachments = [avatarAttachment];

await context.vk.api.wall.post({ owner_id: ownerId, message, attachments });
console.log(trigger.name, 'Neural network post is sent to', communityId, 'community.');
await sleep(trigger.name, (5 * second) / ms);

for (const post of postsToDelete) {
try {
await context.vk.api.wall.delete({ owner_id: ownerId, post_id: post.id });
console.log(trigger.name, `Neural network post ${post.id} is deleted.`);
await sleep(trigger.name, (5 * second) / ms);
} catch (e) {
if (e.code === 104 || e.code === 100) {
console.warn(trigger.name, `Neural network post ${post.id} is not found. It may already be deleted.`);
continue;
}
throw e;
}
}
} catch (err) {
if (err.code === 210) {
console.warn(trigger.name, `Warning: Access to wall's post denied for neural network community ${communityId}.`);
disableNeuralCommunity(communityId);
await sleep(trigger.name, (1 * minute) / ms);
} else if (err.code === 219) {
console.warn(trigger.name, `Warning: Advertisement post was recently added to neural network community ${communityId}.`);
disableNeuralCommunity(communityId);
await sleep(trigger.name, (1 * minute) / ms);
} else if (err.code === 15) {
console.warn(trigger.name, `Warning: Access denied to post to neural network community ${communityId} wall because it was disabled.`);
disableNeuralCommunity(communityId);
await sleep(trigger.name, (1 * minute) / ms);
} else if (err.code === 14) {
console.warn(trigger.name, `Warning: Captcha needed to post to neural network community ${communityId}.`);
await sleep(trigger.name, (1 * minute) / ms);
} else if (err.code === 10) {
console.warn(trigger.name, `Warning: Unknown error occurred while posting to neural network community ${communityId}.`);
await sleep(trigger.name, (1 * minute) / ms);
} else {
throw err;
}
}
}
} catch (error) {
console.error(trigger.name, error);
}
}

const trigger = {
name: "SendNeuralNetworkPagePosts",
action: sendNeuralNetworkPosts
};

module.exports = {
trigger,
neuralNetworkCommunities, // Export for configuration
neuralNetworkPostMessages // Export for testing/customization
};