How can I make this c# method multithreaded? [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I have this c# class that I am trying to make multi-threaded, or able to run 100 threads (requests?) at once.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string[] lines = File.ReadAllLines("C:\\checker/in.txt");
var accCount = File.ReadLines(#"C:\checker/in.txt").Count();
Console.Write("Accounts loaded: " + accCount);
Console.WriteLine();
foreach (string line in lines)
{
string[] account = line.Split(new char[] { ':' });
string user = account[0];
string pass = account[1];
addThreads(user, pass);
Threads.ForEach(t => t.Start());
Console.WriteLine();
}
// Suspend the screen.
Console.ReadLine();
}
public static List<Thread> Threads = new List<Thread>();
public static void addThreads(string user, string pass)
{
var checker = new Checker();
Threads.Clear();
Threads.Add(new Thread(() => { checker.checkAccount(user, pass); }));
Threads.Add(new Thread(() => { checker.checkAccount(user, pass); }));
Threads.Add(new Thread(() => { checker.checkAccount(user, pass); }));
Threads.Add(new Thread(() => { checker.checkAccount(user, pass); }));
Threads.Add(new Thread(() => { checker.checkAccount(user, pass); }));
Threads.Add(new Thread(() => { checker.checkAccount(user, pass); }));
Threads.Add(new Thread(() => { checker.checkAccount(user, pass); }));
}
}
public class Checker
{
//declare vars
string getUsername;
string getMember;
string getAuth;
string check;
public void checkAccount(string username, string password)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
byte[] data = Encoding.ASCII.GetBytes(
$"username={username}&password={password}&mod=www&ssl=1&dest=account_settings.ws");
WebRequest request = WebRequest.Create("https://secure.runescape.com/m=weblogin/login.ws");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (Stream stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
string responseContent = null;
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
using (StreamReader sr99 = new StreamReader(stream))
{
responseContent = sr99.ReadToEnd();
}
}
}
//parse captcha
string patternCaptcha = #"Please\s*complete\s*the\s*reCAPTCHA\s*box";
string inputCaptcha = responseContent;
Match matchCaptcha = Regex.Match(inputCaptcha, patternCaptcha);
string captcha = matchCaptcha.Value;
if (captcha == "Please complete the reCAPTCHA box")
{
captcha = "true";
Console.Write("captcha,captcha,captcha,captcha");
Console.WriteLine();
//return "captcha,captcha,captcha,captcha";
}
else
{
//parse valid/invalid
string patternCheck = #"Your\s*login\s*or\s*password\s*was\s*incorrect";
string inputCheck = responseContent;
Match matchCheck = Regex.Match(inputCheck, patternCheck);
check = matchCheck.Value;
if (check == "Your login or password was incorrect")
{
check = "Invalid";
}
else
{
check = "Valid";
//parse display name
string pattern = #"(<span.*class=.header-top__name.>(.*?)</span>)";
string input = responseContent;
Match match = Regex.Match(input, pattern);
getUsername = match.Groups[2].Value;
byte[] bytes = Encoding.Default.GetBytes(getUsername);
getUsername = Encoding.UTF8.GetString(bytes);
getUsername = getUsername.Replace("?", " ");
//parse member status
string patternMember = #"(Currently\s*Not\s*a\s*Member)";
string inputMember = responseContent;
Match matchMember = Regex.Match(inputMember, patternMember);
getMember = matchMember.Value;
if (getMember == "Currently Not a Member")
{
getMember = "Non Member";
}
else
{
getMember = "Member";
}
//parse auth status
string patternAuthUrl = #"iframe src=\""(.*?)""";
string inputAuthUrl = responseContent;
Match matchAuthUrl = Regex.Match(inputAuthUrl, patternAuthUrl);
string getAuthUrl = matchAuthUrl.Groups[1].Value;
using (WebClient client = new WebClient())
{
string authSource = client.DownloadString(getAuthUrl);
string patternAuth = #"RuneScape\s*Authenticator\s*is\s*disabled";
string inputAuth = authSource;
Match matchAuth = Regex.Match(inputAuth, patternAuth);
getAuth = matchAuth.Value;
if (getAuth == "RuneScape Authenticator is disabled")
{
getAuth = "Auth Disabled";
}
else
{
getAuth = "Authed";
}
}
}
captcha = "false";
string curldata = getUsername + "," + getMember + "," + getAuth + "," + check;
Console.Write(curldata);
Console.WriteLine();
}
}
}
}
Instead of making my program check once per few seconds per post webrequest, how can I make this happen 50-100 times at the same time? Is this possible? Or do I need to do this a different way?

You need to avoid using threads as each thread uses in excess of 1MB of RAM and they are slow to create. You really want to use tasks (TPL) or observables (Rx).
In this case it is quite straight forward to use tasks.
Try this code:
string[] lines = File.ReadAllLines("C:\\checker/in.txt");
var accCount = lines.Count();
Console.Write("Accounts loaded: " + accCount);
Console.WriteLine();
var checker = new Checker();
var tasks =
from line in lines
let account = line.Split(new char[] { ':' })
let user = account[0]
let pass = account[0]
select Task.Factory.StartNew(() => checker.checkAccount(user, pass));
Task.WaitAll(tasks.ToArray());
Console.ReadLine();
That will read the text file and queue up a set of tasks to be run to check each line. The Task.WaitAll pauses the code until all of the tasks are completed.
This make efficient use of the thread-pool so that you're not wasting valuable resources starting up threads.
Your checkAccount is also not thread-safe at the moment. You need to move the field-level variables to be inside your method. It should look something like this:
public void checkAccount(string username, string password)
{
string getUsername;
string getMember;
string getAuth;
string check;

It's pretty simple first you need to do a method to call the threads
public void CallingThreadsMethod()
{
ThreadStart ts = new ThreadStart(SomeFunction);
Thread t = Thread(ts);
t.IsBackground = true;
t.Start();
}
void Somefunction(){}
or if you want many threads you can make a thread list
public static List<Thread> Threads = new List<Thread>();
public static void addThreads()
{
Threads.Clear();
Threads.Add(new Thread(Method1));
Threads.Add(new Thread(Method2));
}
And start it in you'r main function
Vars.addThreads();
Vars.Threads.ForEach(t => t.Start());
but if you're using windows forms or wpf i recommend using BackgroundWorkers

Related

How to skip System.InvalidOperationException when application running on C#?

I have used thread , task function to remove UI block after method is executing. I used following code but it gave me System.InvalidOperationException. Here is code I tried:
private void FindVariation()
{
string[] idlist = richTextBox7.Text.Split('\n'); // < == System.InvalidOperationException
// foreach (string id in idlist)
for (int i = 0; i < Convert.ToInt32(idlist.Length); i++)
{
string url = "http://www.ebay.com/itm/" + idlist[i];
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream());
// richTextBox2.Text += sr.ReadToEnd();
string a = sr.ReadToEnd();
sr.Close();
string source = null;
source = string.Join(Environment.NewLine,
a.Split(',', ']', '[')
.Where(m => m.Length == 12 && m.All(char.IsDigit)));
if (string.IsNullOrEmpty(source))
{
// MessageBox.Show("String is null");
}
else
{
richTextBox6.Text += idlist[i] + Environment.NewLine;
}
richTextBox1.Text = richTextBox7.Text;
}
}
Task fv = new Task(FindVariation);
fv.Start();
Could anybody show me what is the error?
Here's the kind of approach you need:
async Task Main()
{
string input = richTextBox7.Text;
string result = await Task.Run(() => FindVariation(input));
richTextBox1.Text = result;
}
private string FindVariation(string richTextBox7Text)
{
string[] idlist = richTextBox7Text.Split('\n');
//your code - but no access to any UI element.
return result;
}
The key thing is that inside the FindVariation you cannot access or update any UI element. Everything has to be passed in or passed out before UI is updated.

Call method inside multiple tasks C#

I am having a problem calling a method in a list of Tasks. I have a method that creates N number of tasks. In each task I perform some operations that in the end results an fetching data via HttpWebRequest and writing that data into a file. I use lock objects to lock the access to shared resources like variables. Everything performs great except the call for a method that creates a executes an HttpWebRequest (method GetData). Whenever I don't lock that call for the method (GetData) it seems that some data/files are skipped. For example:
With the lock object I get file 1,2,3 and 4
Without the lock object I get file 2,4 and 3
Here's the code for the method that creates the tasks
private object lockObjectWebRequest= new object();
private object lockObjectTransactions = new object();
public List<Task> ExtractLoanTransactionsData(string URLReceived, string Headers, string Body)
{
List<Task> Tasks = new List<Task>();
try
{
int Limit = 0;
int OffsetItemsTotal = 0;
int NumberOftasks = 4;
// Create the task to run in parallel
for (int i = 0; i <= NumberOftasks; i++)
{
int OffsetCalculated = 0;
if (i > 0)
{
OffsetCalculated = Limit * i;
}
Tasks.Add(Task.Factory.StartNew(() =>
{
string URL = URLReceived+ "&offset=" + OffsetCalculated .ToString() + "&limit=" + Limit.ToString();
string Output = string.Empty;
lock (lockObjectWebRequest)
{
Output = GetData(URL, Headers,Body);
}
if (Output != "[]")
{
lock (lockObjectTransactions)
{
Identifier++;
Job.Identifier = Identifier;
// write to file
string json = JValue.Parse(Output).ToString(Formatting.Indented);
string FileName = OffSet.ToString() + Identifier;
string Path = #"C:\FileFolder\" + FileName + ".json";
File.WriteAllText(Path, json);
}
}
}));
}
}
catch (Exception ex)
{
Tasks = new List<Task>();
}
return Tasks;
}
Here's the code that performs the HttpWebRequest:
public string GetData(string URL, string Headers, string Body)
{
string Data = string.Empty;
Headers = Headers.Trim('{').Trim('}');
string[] HeadersSplit = Headers.Split(new char[] { ',', ':' });
HttpWebRequest WebRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
WebRequest.Credentials = new NetworkCredential();
WebRequest.Method = "POST";
HttpWebResponse WebResponse;
// Set necessary Request Headers
for (int i = 0; i < HeadersSplit.Length; i = i + 2)
{
string HeaderPart1 = HeadersSplit[i].Replace("\"", "").Trim();
string HeaderPart2 = HeadersSplit[i + 1].Replace("\"", "").Trim();
if (HeaderPart1 == "Content-Type")
{
WebRequest.ContentType = HeaderPart2;
}
else if (HeaderPart1 == "Accept")
{
WebRequest.Accept = HeaderPart2;
}
else if (HeaderPart1 == "Authorization")
{
WebRequest.Headers["Authorization"] = HeaderPart2;
}
}
WebRequest.Headers.Add("cache-control", "no-cache");
// Add body to Request
using (var streamWriter = new StreamWriter(WebRequest.GetRequestStream()))
{
streamWriter.Write(Body);
streamWriter.Flush();
streamWriter.Close();
}
// Execute Request
WebResponse = (HttpWebResponse)WebRequest.GetResponse();
// Validate Response
if (WebResponse.StatusCode == HttpStatusCode.OK)
{
using (var streamReader = new StreamReader(WebResponse.GetResponseStream()))
{
Data = streamReader.ReadToEnd();
}
}
return Data;
}
What am I doing wrong here? The method doesn't have global data that is shared between tasks.
But you do have data that is shared between the tasks: the local varibleIdentifier and the method argument Job.
You are writing to a file using the Identifier in the file-name. If the lock is not in place, that piece of code will be running simultaniously.
The implications for Job can't be deduced from your question.
I think you can solve the identifier problem by doing this:
var identifier = Interlocked.Increment(ref Identifier);
Job.Identifier = identifier; // Use 'identifier', not 'Identifier'
// write to file
string json = ...;
string FileName = OffSet.ToString() + "_" +
"MAMBU_LT_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "_" +
identifier; // Use 'identifier', not 'Identifier'
...

Using parallelism to speed up the code

I've made a tool that sends HTTP requests (GET) (reads the info of what to send from a .txt), captures the json and parses it + writes it to a .txt. The tool is a console app targetting .NET Framework 4.5.
Could I possibly speed this up with the help of "multi-threading"?
System.IO.StreamReader file =
new System.IO.StreamReader(#"file.txt");
while ((line = file.ReadLine()) != null)
{
using (var client = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }))
{
client.BaseAddress = new Uri("https://www.website.com/");
HttpResponseMessage response = client.GetAsync("index?userGetLevel=" + line).Result;
response.EnsureSuccessStatusCode();
string result = response.Content.ReadAsStringAsync().Result;
dynamic json = JObject.Parse(result);
string level = json.data.level;
if (level == "null")
{
Console.WriteLine("{0}: User does not exist.", line);
}
else
{
Console.WriteLine("{0}: User level is {1}.", line, level);
using (StreamWriter sw = File.AppendText(levels.txt))
{
sw.WriteLine("{0} : {1}", line, level);
}
}
}
}
file.Close();
Answers to questions:
"Speed up what?": I'd like to speed up the whole process (the amount of requests it sends each time, not how fast it sends them.
"How many requests?": The tool reads a string from a text file and puts that information in to a part of the URL, and then captures the response, then places that in a result.txt. I'd like to increase the speed it does this at/how many it does at a time.
"Can the requests happen concurrently or are dependant on prior request responses?": Yes, and no, they are not dependent.
" Does your web server impose a limit on the number of concurrent requests?": No.
"How long does a typical request take?": The request + the time the response shows up on console per each requests is a little bit more than 1/3 of a second.
There are several ways to asymmetric programming, one of them could be something like this:
var messages = new ConcurrentQueue<string>();
//or
//var lockObj = new object();
public int main()
{
var fileText = File.ReadAllLines(#"file.txt");
var taskList = new List<Task>();
foreach (var line in fileText)
{
taskList.Add(Task.Factory.StartNew(HandlerMethod, line));
//you can control the amount of produced task if you want:
//if(taskList.Count > 20)
//{
// Task.WaitAll(taskList.ToArray());
// taskList.Clear();
//}
}
Task.WaitAll(taskList.ToArray()); //this line may not work as I expected.
//for the first way
var results = new StringBuilder();
foreach (var msg in messages)
{
results.AppendLine("{0} : {1}", line, level);
}
File.WriteAllText("path", results.ToString());
}
For writing the results, either you can use a public concurrent collection or use a lock pattern:
public void HandlerMethod(object obj)
{
var line = (string)obj;
var result = string.Empty;
using (var client = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }))
{
client.BaseAddress = new Uri("https://www.website.com/");
HttpResponseMessage response = client.GetAsync("index?userGetLevel=" + line).Result;
response.EnsureSuccessStatusCode();
string result = response.Content.ReadAsStringAsync().Result;
dynamic json = JObject.Parse(result);
result = json.data.level;
}
//for the first way
if (string.IsNullOrWhiteSpace(result))
{
messages.Enqueue("{0}: User does not exist.", line);
}
else
{
messages.Enqueue("{0}: User level is {1}.", line, result);
}
//for the second way
//lock(lockObj)
//{
// using (StreamWriter sw = File.AppendText(levels.txt))
// {
// sw.WriteLine("{0} : {1}", line, level);
// }
//}
}

Filtering text from string

I have it now like this:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
public class Program
{
static void Main(string[] args)
{
//Consider making this configurable
const string sourceFile = "test2.txt";
const string pattern = "http://10.123.9.66:80";
//var FirstSeparatorLastNameExact = new[] { "nosyn_name_last_exact:(qxq" };
//var SecondSeparatorLastNameExact = new[] { "qxq)" };
string[] FirstSeparator = new string[] { "nosyn_name_last_exact:(qxq" };
string[] SecondSeparator = new string[] { "qxq)" };
string[] FirstSeperatorFirstName = new string[] {"nosyn_name_first_exact:(qxq"};
string[] secondSeperatorFirstName = new string[]{"qxq)"};
Regex re = new Regex("^(http|https)://");
HttpWebResponse response;
// var webClient = new WebClient();
var times = new Dictionary<string, TimeSpan>();
var stopwatch = new System.Diagnostics.Stopwatch();
//Add header so if headers are tracked, it will show it is your application rather than something ambiguous
//webClient.Headers.Add(HttpRequestHeader.UserAgent, "Response-Tester-Client");
var urlList = new List<string>();
//Loop through the lines in the file to get the urls
try
{
stopwatch.Start();
using (var reader = new StreamReader(sourceFile))
{
while (!reader.EndOfStream)
{
var urNewList = new List<string>();
var line = reader.ReadLine();
//line = line.Substring(line.IndexOf(pattern));
//line.Split("\t");
var columns = line.Split('\t');
//var result = line.Split(Seperator, StringSplitOptions.RemoveEmptyEntries)[1].Split(')')[0];
if (columns[2] == "R")
{
var url = columns[4] + "?" + columns[5];
urlList.Add(url);
Thread.Sleep(250);
}
//if (line.Contains(result))
//{
//MatchCollection matches = Regex.Matches(line, lastName);
//foreach (string lines in File.ReadLines(sourceFile))
//{
//var LastNameSearch = line.Split(FirstSeparatorLastNameExact, StringSplitOptions.RemoveEmptyEntries)[1];
//var resultLastNameSearch = LastNameSearch.Split(FirstSeparatorLastNameExact, StringSplitOptions.RemoveEmptyEntries)[0];
//var temp = line.Split(FirstSeparator, StringSplitOptions.RemoveEmptyEntries)[1];
//var result2 = temp.Split(SecondSeparator, StringSplitOptions.RemoveEmptyEntries)[0];
//Console.WriteLine(result2);
string[] result = line.Split(FirstSeperatorFirstName, StringSplitOptions.RemoveEmptyEntries);
if (result.Length > 2)
{
string[] inner = result[1].Split(')');
if (inner.Length > 1)
{
Console.WriteLine(inner[0]);
Console.WriteLine(result);
}
}
//var split = line.Split(FirstSeperatorFirstName, StringSplitOptions.RemoveEmptyEntries);
//if (split.Length > 1)
//{
// Console.WriteLine(split[1].Split(')')[0]);
// // Console.WriteLine(split);
//}
}
}
}
catch (Exception e)
{
Console.WriteLine("An error occured while attempting to access the source file at {0}", sourceFile);
}
finally
{
//Stop, record and reset the stopwatch
stopwatch.Stop();
times.Add("FileReadTime", stopwatch.Elapsed);
stopwatch.Reset();
}
//Try to connect to each url
var counter = 1;
foreach (var url in urlList)
{
try
{
stopwatch.Start();
using (WebClient webClient = new WebClient())
{
webClient.Headers.Add(HttpRequestHeader.UserAgent, "Response-Tester-Client");
// HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
//webClient.Dispose();
}
}
catch (Exception e)
{
Console.WriteLine("An error occured while attempting to connect to {0}", url);
}
finally
{
stopwatch.Stop();
//We use the counter for a friendlier url as the current ones are unwieldly
times.Add("Url " + counter, stopwatch.Elapsed);
counter++;
stopwatch.Reset();
}
}
//Release the resources for the WebClient
//webClient.Dispose();
//Write the response times
Console.WriteLine("Url " + "\t\t\t\tLast Name");
foreach (var key in times.Keys)
{
Console.WriteLine("{0}: {1}", key, times[key].TotalSeconds);
}
Console.ReadKey();
}
}
}
But I still get the error: Index was outside the bounds of the array. So how to change it? And in fact It also has to be remove the qxq. I try it with two string that I declared above
Thank you
how to skip the line if there is no nosyn_name_first_exact
Split your code to not assume there is one. You have:
test.Split(FirstSeperatorFirstName, StringSplitOptions.RemoveEmptyEntries)[1]
.Split(')')[0]
.Dump();
Change it to something like:
var split = test.Split(FirstSeperatorFirstName, StringSplitOptions.RemoveEmptyEntries);
if (split.Length > 1)
{
split[1].Split(')')[0].Dump();
}
You see the same problem can occur within the if, repeat if necessary.
You are trying to fetch a index from an array that do not have that many values (index is out of bounds).
Check if the array is long enough before you try to fetch the index, like:
string[] result = test.Split(FirstSeperatorFirstName, StringSplitOptions.RemoveEmptyEntries);
if(result.Length > 2) {
string[] inner = result[1].Split(')');
if(inner.Length > 1) {
inner[0].Dump();
}
}
Or check if the string contains the given substring before even trying to split:
if(test.Contains("nosyn_name_first_exact:(qxq")) {
// Split and do whatever.
}

How to connect to Mailman mailing list using .Net

I have to develop a .Net application in which i have to add or remove a user from Mailman mailing list.My Question is whether there is any .Net connector or Dll to connect to mailman mailing list using .Net.
Edit (9/21/14): I have just released a NuGet package for manipulating most aspects of a Mailman v2 list via HTTP calls. https://www.nuget.org/packages/MailmanSharp/
I'm not aware of any existing component to do this, but since the Mailman interface is all on the web, you can "control" it with HttpWebRequest; I recently wrote a small app which can retrieve the subscriber list, subscribe/unsubscribe people, and set individual flags like moderate/nomail/etc. It takes a little poking around in the source of the Mailman pages to see what variables need to be set in the POST, and some trial and error. I suggest setting up a temp Mailman list just to play with.
In order to do most of this, you'll need a persistent CookieContainer that you can hook up to your different HttpWebRequests; the first call is a POST to the admin page with the admin password to set the session cookie that gives you access to the other pages.
Some of the POSTs are regular application/x-www-form-urlencoded types, but some are also multipart/form-data. For the latter, I found some very helpful code at http://www.briangrinstead.com/blog/multipart-form-post-in-c I had to make a couple of changes so that I could pass in my CookieContainer
Here's some sample code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Data;
using System.Threading;
namespace UpdateListserv
{
class Program
{
static void Main(string[] args)
{
try
{
File.Delete(_logFilename);
Log(String.Format("Starting: {0}", DateTime.Now));
Login();
var roster = GetSubscribers();
Unsubscribe(roster);
string members = GetMemberEmails();
Subscribe(members);
Unmoderate("foo#example.com");
Log("Done");
}
catch(Exception e)
{
Log(e.Message);
}
}
private static void Unmoderate(string email)
{
Log("Unmoderating " + email);
email = email.Replace("#", "%40");
_vars.Clear();
_vars["user"] = email;
_vars[email + "_nomail"] = "off";
_vars[email + "_nodupes"] = "on";
_vars[email + "_plain"] = "on";
_vars[email + "_language"] = "en";
_vars["setmemberopts_btn"] = "Submit Your Changes";
FormUpload.MultipartFormDataPost(_adminUrl + _membersPage, "foobar", _vars, _cookies);
}
private static CookieContainer _cookies = new CookieContainer();
private static string _adminUrl = "http://mylist.com/admin.cgi/listname";
private static string _rosterUrl = "http://mylist.com/roster.cgi/listname";
private static string _pw = "myPassword";
private static string _adminEmail = "foo#example.com";
private static Dictionary<string, object> _vars = new Dictionary<string, object>();
private static string _addPage = "/members/add";
private static string _removePage = "/members/remove";
private static string _membersPage = "/members";
private static string _logFilename = "Update Listserv.log";
private static void Log(string message)
{
Console.WriteLine(message);
using (var log = File.AppendText(_logFilename))
log.WriteLine(message);
}
private static void Subscribe(string members)
{
// members is a list of email addresses separated by \n
Log("Subscribing everyone");
_vars.Clear();
_vars["subscribees"] = members;
_vars["subscribe_or_invite"] = 0;
_vars["send_welcome_msg_to_this_batch"] = 0;
_vars["send_notifications_to_list_owner"] = 0;
FormUpload.MultipartFormDataPost(_adminUrl + _addPage, "foobar", _vars, _cookies);
}
private static string GetMemberEmails()
{
// This method retrieves a list of emails to be
// subscribed from an external source
// and returns them as a string with \n in between.
}
private static void Unsubscribe(string roster)
{
// roster is a list of email addresses separated by \n
Log("Unsubscribing everybody");
_vars.Clear();
_vars["unsubscribees"] = roster;
_vars["send_unsub_ack_to_this_batch"] = 0;
_vars["send_unsub_notifications_to_list_owner"] = 0;
FormUpload.MultipartFormDataPost(_adminUrl + _removePage, "foobar", _vars, _cookies);
}
private static string GetSubscribers()
{
// returns a list of email addresses subscribed to the list,
// separated by \n
Log("Getting subscriber list");
var req = GetWebRequest(_rosterUrl);
req.Method = "post";
_vars.Clear();
_vars["roster-email"] = _adminEmail;
_vars["roster-pw"] = _pw;
var rosterLines = GetResponseString(req).Split('\n').Where(l => l.StartsWith("<li>"));
Log(String.Format("Got {0} subscribers", rosterLines.Count()));
var roster = new List<string>();
var regex = new Regex("<a.*>(.*)</a>");
foreach (var line in rosterLines)
{
roster.Add(regex.Match(line).Groups[1].Value.Replace(" at ", "#"));
}
return String.Join("\n", roster);
}
private static void Login()
{
Log("Logging in to list admin panel");
var req = GetWebRequest(_adminUrl);
req.Method = "post";
_vars["adminpw"] = _pw;
SetPostVars(req);
req.GetResponse();
}
private static HttpWebRequest GetWebRequest(string url)
{
var result = HttpWebRequest.Create(url) as HttpWebRequest;
result.AllowAutoRedirect = true;
result.CookieContainer = _cookies;
result.ContentType = "application/x-www-form-urlencoded";
return result;
}
private static string GetResponseString(HttpWebRequest req)
{
using (var res = req.GetResponse())
using (var stream = res.GetResponseStream())
using (var sr = new StreamReader(stream))
{
return sr.ReadToEnd();
}
}
private static void SetPostVars(HttpWebRequest req)
{
var list = _vars.Select(v => String.Format("{0}={1}", v.Key, v.Value));
using (var stream = req.GetRequestStream())
using (var writer = new StreamWriter(stream))
{
writer.Write(String.Join("&", list));
}
}
}
}

Categories