I'm sorry it took a little longer than I anticipated but I'm in the 6-8 days range so....
tl;dr; You can't see your flagging history across sites
There is no current functionality on your account (network profile) that let you see all your flags across all sites. As flags are private this info is also not available in the Stack Exchange Data Explorer. Looking at the StackAPI it turns out that even that endpoint keeps the flags private. As a consequence users of the native Android and iOS apps are not able to check their flag status in the app (this is confirmed by @ShadowWizard)
That leaves only one option to get this solved today: (web)scraping. It is not hard to do but becomes a challenge if the pages you need to scrape are behind a login, which is true for the flag-summary page.
I have ventured into three directions. The first was an userscript. The cross-site scripting security measures didn't make this option feasible at all. The second was a commandline tooling solution. The third a c# solution.
All here presented solutions require an Stack Exchange account. Figuring out how to authenticate with Google/facebook or any other (open)id provider is left for future revisions.
Command line
The tool curl is available on several platforms, including windows. I wrote a small windows command script that for a particular site:
- does an initial get of the users/login route
- gets the o so important fkey
- posts the username and the password AND the fkey to users/login
- get the /users/flag-summary
Curl is capable in writing its output to a file and that is basically where I stopped. I couldn't find a quick and reliable way without adding extra tools to parse the html result and transform the data into information. Here is the script in all its glory:
set [email protected]
set [email protected]
rem output from SEDE query
rem https://data.stackexchange.com/stackoverflow/query/321563/find-all-userids-across-databases-for-an-account-id
call :singlesite https://meta.stackexchange.com 269324
call :singlesite https://stackoverflow.com 3972324
rem end output from sede query
goto :EOF
:singlesite
curl -k -c so.cookies -o login.html %1/users/login
findstr /C:"input type=\"hidden\" name=\"fkey\"" login.html > fkey.txt
for /F "delims==> tokens=4" %%a in (fkey.txt) do call :fkeyp "%%a"
curl -v -L -k -c so.cookies -o login-post.html -d fkey=%fkey% --data-urlencode email%SEEmail% --data-urlencode password%SEPwd% %1/users/login
curl -v -k -b so.cookies -o %2-flags.html %1/users/flag-summary/%2
goto :EOF
:fkeyp
set p1=%1
set fkey=%p1:"=%
rem echo %fkey%
goto :EOF
For the script to work properly you need to store your username in a text called se-user.txt
and your password in se-pwd.txt
. You need to run this SEDE query with your own accounted (found on your network profile) so the script only scrapes sites that you have an account on.
C# tooling
As the parsing and transformation became an obstacle I switched to a c# program where I could use CsQuery. I will not include all details here, I created a github repository for that. A couple of things to point out:
To get the associated accounts we can leverage the StackAPI:
/// <summary>
/// Call the StackApi https://api.stackexchange.com/docs/associated-users
/// </summary>
/// <param name="accountid">your account id</param>
/// <returns>network-users</returns>
public List<network_user> GetAssociatedAccounts(int accountid)
{
var users = new List<network_user>();
using( var wc = new EnhancedWebclient() )
{
var serializer = new DataContractJsonSerializer(typeof(Wrapper));
using (var ms = new MemoryStream(
wc.DownloadData(
String.Format("https://api.stackexchange.com/2.2/users/{0}/associated?pagesize=100&filter=!6PboxJ(eCNnSz", accountid))))
{
var wrapper = (Wrapper) serializer.ReadObject(ms);
foreach(var user in wrapper.items)
{
users.Add(user);
}
}
}
return users;
}
and the fetching, parsing and transforming of the flag-summary:
/// <summary>
/// Visit flag-summary for a user
/// </summary>
/// <param name="user">the network user</param>
/// <returns>all flags on the first page</returns>
public List<Flag> GetFlags(network_user user)
{
var flags = new List<Flag>();
// get the flag-summary page and CQ
var flagSummary = CQ.Create(
Get(String.Format(
"{0}/users/flag-summary/{1}",
user.site_url,
user.user_id)));
// itererate over each flaggesd post
flagSummary
.Find("#mainbar div.flagged-post")
.Each(ele => {
// handle a single post
var flagdata = new Flag();
var flagpost = CQ.Create(ele);
// the flagged post
var post = flagpost.Find("div.answer-link a");
flagdata.Url = user.site_url + post.Attr("href");
flagdata.Title = post.Text();
// the OP (and date)
var op = flagpost.Find("div.post-user-info span").Attr("title");
DateTime creationdate;
DateTime.TryParse(op, out creationdate);
flagdata.CreationDate = creationdate;
// spam flags look different
// our own flag data
var me = flagpost.Find("div.cbt");
var time = me.Find("span.relativetime").Attr("title");
DateTime flagdate;
DateTime.TryParse(time, out flagdate);
flagdata.FlagDate = flagdate;
// in the collection
flags.Add(flagdata);
});
return flags;
}
It is all controlled by a simple commandline tool that takes 3 arguments (or an userinfo.user file)
const int username = 0;
const int password = 1;
const int accountid = 2;
// ONLY WORKS WITH AN STACK EXCHANGE ID!
var client = new ClientStack(args[username], args[password]);
client.Login();
// collect all flags
var flags = new List<Flag>();
// ask the api to give all associated accounts
foreach (var user in client.GetAssociatedAccounts(Int32.Parse(args[accountid])))
{
// login per site
client.LoginSite(user.site_url);
// visit the first page of flags
flags.AddRange(client.GetFlags(user));
}
// order all flags, most recent first
var recentFlags = from flag in flags
orderby flag.FlagDate descending
select flag;
// output to the console
foreach(var recentFlag in recentFlags)
{
Console.WriteLine("\"{0}\",\"{1}\",\"{2}\"",recentFlag.FlagDate, recentFlag.CreationDate, recentFlag.Url);
}
If run the results are ordered by most recent flagged posts (my test account doesn't have spam flags and those look different in the html but I don't expect this to break). The output is comma separated so it's easy to be consumed by Excel.
The result will look like this:
Conclusion
It is quite an hassle to get your flagged-posts networkwide. Although the presented solution do work to some degree, a feature request to implement this in the site or in the API would be very helpful.