Good day to all!

I am the administrator of one music group VKontakte (hereinafter - VK). Musicians often go on tour in different cities of Russia and the CIS countries. One of the ways to notify fans of the group about the upcoming concert in their city is to send invitations to the appropriate VK meeting.
In the article I want to show one of the possible solutions to this problem.
I hope that this article will be useful to other group administrators, the number of user invitations to events not in their city will decrease and more people will unlock the opportunity to invite them to meetings!
First of all, you must be the administrator of the VC group and this group must be the organizer of the VC meeting.
')
Decision "To do everything manually"
The existing interface allows you to manually create such a newsletter through the meeting menu:
“Invite friends” - “Invite group members”:

The disadvantages of this solution are obvious:
- Long, because in some cities the number of participants is more than a few thousand
- Unable to execute invitations to members residing in a particular city.
Solution “Automate the process”
There are two approaches here: write an application using
VK API or standalone via post-get requests.
After analysis, I realized that the VC API is not suitable. In the description there is no method of inviting the user to a meeting, and I don’t really want to contact the registration of the application and other internal VK rules.
Therefore, you will have to analyze the post-get requests, and write a “simulator” of the user.
Consider the main stages of work:
- User Authorization
- Getting group members in a specific city
- Mailing invitations
Authorization
With login everything is quite simple - POST request at login.vk.com:
act=login&q=1&al_frame=1&expire=1&captcha_sid=&captcha_key=&from_host=vk.com&from_protocol=http&email=USERNAME&pass=PASSWORD
In response, we receive the location parameter with the address to which the redirect will be performed:
vk.com/login.php?act=slogin&al_frame=1&hash=bd7aed27961c325b407332b5855fa1c1&s=1
After the redirect, the remixsid parameter is written to the cookie - a session identifier that confirms the successful authorization
Fans from city N
For a list of group members from a specific city, we use the standard search vk.com/search, this filter set is especially important to us:

You can search for group members from the group’s page, and the query will look like this:
al_search.php?al=1&c[group]=6206&c[section]=people
where group is the group id.
After adding filtering by country and city, the request will look like:
al_search.php?al=1&c[city]=1&c[country]=1&c[group]=6206&c[section]=people
where, respectively, city - city id, country - country id
The answer to the request is a list of users.
The response header carries two important meanings:
"has_more":true,"offset":200
has_more - determines whether users will still be issued
offset - "indent" or shift from the first user
The block with information about a single user is:
<div class="people_row three_col_row clear_fix"> <div class="img search_bigph_wrap fl_l" onmouseover="Searcher.bigphOver(this, 1746355)"> <a href="/romasladky" onclick="return nav.go(this, event);"><img class="search_item_img" src="http://cs425828.vk.me/v425828355/11c7e/WlD9D-hBeJI.jpg" /></a> </div> <div class="info fl_l"> <div class="labeled name"><a href="/romasladky" onclick="return nav.go(this, event);"> </a></div><div class="labeled "></div><div class="online">Online</div> </div>
In this block, interesting data:
Parsit answer is quite convenient using regular expressions.
For id use the following expression:
"<div[^>]*onmouseover=\"Searcher.bigphOver\\(this, (\\d+)\\)\">"
To get the name and href:
"<div class=\"labeled name\"><a href=\"/([^\"]+)\" onclick=\"return nav\\.go\\(this, event\\);\">([^<]+)<";
In this approach, there is one synthetic limitation - the contact does not give out more than 1000 search results. This is critical, because, for example, in Moscow a group of 3000+ participants. To circumvent this limitation, you will have to add additional filtering on users, and then combine the results of all filters.
Of the available, you need to select only those filters whose values ​​are fixed and not so many.
For this task fit:
Gender - [sex], Values: 0-2
Sort order - [sortId], values ​​0-1
Marital status - [statusId], values ​​0-7.
On ruby, this search looks like this:
offset=0 for sort_id in 0..1 do for status_id in 0..7 do for sex_id in 0..2 do offset=0 begin get_str = "/al_search.php?al=1" get_str += "&c[city]=#{city_id}" if city_id.to_i>0 get_str += "&c[country]=#{country_id}" if country_id.to_i>0 get_str += "&c[group]=#{group_id}" get_str += "&c[name]=1&c[section]=people" get_str += "&c[sex]=#{sex_id}" if sex_id>0 get_str += "&c[sort]=#{sort_id}" if sort_id>0 get_str += "&c[status]=#{status_id}" if status_id>0 get_str += "&offset=#{offset}" if offset>0 ...
This will get a list of all users in the city.
If we send the invitations manually, we note that it is sent using the POST method:
act=a_invite&al=1&gid=65898108&hash=99247d766b77d7a584&mid=22935
where gid is the meeting id, mid is the user id, hash is a certain hash that carries information about the inviter. This hash should now be obtained for all users from our list.
Getting hash
To get the hash, you will have to parse the list of “friends” that I can invite to the meeting. Friends are quoted in quotes, because here all members of the organizing group speak under this concept.
The GET request for getting this list looks like this:
vk.com/friends?act=get_section_friends&al=1&gid=65898108&offset=0§ion=members&sugg_rev=0
where gid - meeting id
The answer will be a json-string with a set of blocks of the form:
['1298','http://cs315422.vk.me/u01298/d_e13351b2.jpg','/id1298','2','0',' ','0','1','61','','0','2d9d4211c2297c3a06']
where the 1st parameter is the user id, and the last is the hash we need.
Processing of this data is performed using a regular expression:
@"\['(\d*)','[^']*','\/([\w\.]+)','[^']*','[^']*','([^']+)','[^']*','(\d+)','[^']*','[^']*','[^']*','(\w+)'\]"
The format of this answer changes quite often, so the program did the trick: in addition, the number of links to avatars (2-parameter) is calculated and the number of avatars and users match after processing.
On C #, this check looks like this:
string patternNorm = @"'http:\/\/cs\d+\.vk\.me"; string patternDeactiveOrDeleted = @"'\/images\/\w+\.gif'"; MatchCollection mcNorm = Regex.Matches(responseString, patternNorm); MatchCollection mcDeactiveOrDeleted = Regex.Matches(responseString, patternDeactiveOrDeleted); int httpCount = mcNorm.Count + mcDeactiveOrDeleted.Count; if (listVkUser.Count != httpCount) { throw new Exception("http total count != user count"); }
After processing the list of "friends" and merging it with a list of users from the city, everything is ready for sending out invitations.
During the mailing process, it was found out that after every 40 invitations you need to enter a captcha, a link to a captcha is given in the form:
captcha.php?sid={1}&s=1
where sid is the unique id of this captcha.
With each request for this url will be issued a new image with the captcha.
Here is the only place where the process requires user input and manual input.
VkInviter program
To automate the above actions written program VkInviter.
The main program window is shown in the screenshot:

In addition to the described algorithm, the program allows you to sample for several cities, which is important when invitations are sent not only in the city, but also in the nearest regions.
The source code is laid out on
github , the
ruby script is also laid out, which can be useful for understanding general logic.
Conclusion
In conclusion, I want to say a few words about efficiency.
I noticed that approximately 60% of all users were forbidden to invite themselves to meetings.
About 10% of those who accepted the invitation to the meeting come.
I do not know how long this functionality will remain in the VC and why the opportunity to invite everyone was cut out a few years ago.
The source code can be used by anyone at their discretion.