Find our missing dead

19

Challenge

Peter has risen again to save us from the duplicate challenges!

Peter Taylor is dead, and there's no doubt about it (well, apart from the huge amount of doubt we have... but just ignore that). In his memory, you must write a program which determines whether a given user is alive or dead.

Further Information

A user is dead if they haven't been seen for more than a day, any less than that then they are alive. Check the last seen section found here:

Location of last seen

The input will be a user id (for example, mine is 30525, and Peter Taylor's is 194). Assume that all inputs are valid PPCG IDs.

If the user is alive, you should output:

[User name] is alive!

Where you replace [User name] for their username not their user id.

If the user is dead, you should output:

 Sadly, [User name] is dead.

T-SQL entrants using the SE Data Explorer are disallowed.

Winning

The shortest program in bytes wins.

Leaderboard

var QUESTION_ID=59763;OVERRIDE_USER=30525;function answersUrl(e){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"http://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

Beta Decay

Posted 2015-10-05T17:39:12.293

Reputation: 21 478

2Why disallow T-SQL/SEDE? – Geobits – 2015-10-05T17:42:32.427

1Needs a description of where to find the number for "last seen". – feersum – 2015-10-05T17:42:56.103

@feersum It is the title tag of the last seen value. – mınxomaτ – 2015-10-05T17:46:41.657

@feersum Good enough? – Beta Decay – 2015-10-05T17:46:59.667

1I think [tag:parsing] is more relevant to this than [tag:internet]. – mınxomaτ – 2015-10-05T20:41:46.843

@minxomat Edited tags – Alex A. – 2015-10-05T20:43:11.337

Be forewarned, this challenge has an air of Schrödinger's cat to it. If you find a user to be dead before someone else, you may be responsible/liable for killing them, in a sense. – Albert Renshaw – 2015-10-06T00:07:22.693

Answers

4

CJam, 115 bytes

"codegolf.xyz/u/"r+g_N/{"s=\"r"/_0='>&!*1>s_"da"#)\_el=!|}#)"Sadly, %s is dead.""%s is alive!"?\"\"User "/1='"/1<e%

The idea is the same as in my Bash answer, except that this answer doesn't use regular expressions, because CJam doesn't have regular expressions...

The online interpreter doesn't perform web requests, so this will only work from the command line.

Dennis

Posted 2015-10-05T17:39:12.293

Reputation: 196 637

8

Bash, 151 bytes

r="curl -L codegolf.xyz/u/$1";u=`$r|grep -Pom1 '(?<="User )[^"]*'`
$r|grep -Pq '^[^>]*s="r.*(da|[A-Z])'&&echo "Sadly, $u is dead."||echo "$u is alive!"

As usual, output to STDERR is ignored.

Example run

$ ./is-dead 30525 2>&-
Beta Decay is alive!
$ ./is-dead 44935 2>&-
Sadly, Alpha Decay is dead.

Idea

This code greps for lines containing s="r that do not have > before its occurrence. For example:

Last seen <span title="2015-09-17 12:00:00Z" class="relativetime">just now</span>
  • If the match is followed by the string da, it contains the word yesterday or days (as in 2 days ago).
  • If the match is followed by an uppercase letter, it contains the name of a month.
  • In all other cases, the user is undead alive.

The user's name is extracted from a Twitter meta tag. Example:

<meta name="twitter:title" property="og:title" itemprop="title name" content="User Dennis">

Dennis

Posted 2015-10-05T17:39:12.293

Reputation: 196 637

3I've updated my profile, and suddenly I'm dead. :( – TessellatingHeckler – 2015-10-06T01:24:58.367

5You are no longer dead to me. – Dennis – 2015-10-06T03:04:41.783

1That's a relief! – TessellatingHeckler – 2015-10-06T03:26:54.623

Who registered that domain? – jimmy23013 – 2015-10-06T05:38:14.290

@jimmy23013 minxomat

– Dennis – 2015-10-06T05:38:54.213

7

PowerShell (v4), 228 217 209, 157 bytes

$u=($x=curl "codegolf.xyz/u/$args").BaseResponse.ResponseURI.Segments[-1]
if($x-match'n <(.*?)((c|n|ur)s* ago|w)<'){"$u is alive!"}else{"Sadly, $u is dead."}

e.g.

PS C:\> test.ps1 30525
beta-decay is alive!

C:\> test.ps1 67
Sadly, keith-randall is dead.


#Previous 209 byte version:
$f={$u=((curl "api.stackexchange.com/2.2/users/$($args)?&site=codegolf"
)|ConvertFrom-Json).Items;$d=$u.display_name;if((get-date -U %s)-
$u.last_access_date-gt86400){"Sadly, $d is dead."}else{"$d is alive!"}}

(Sorry, Keith Randall, you were just the first account I found with >1 day since last seen date).

I was happy with the solid, reliable 209 byte one calling the API, but screen-scraping is the way to go for golf.

  • This now pulls the username from the redirected URI - but it is a name rather than a numeric ID, as required.

  • And it matches the line Last seen <span title="2015-10-03 13:15:38Z" class="relativetime">2 days ago</span> with n <... [nr]s ago< trying to catch sec(s) ago, min(s) ago, hour(s) ago, and just now, and miss "days weeks, months" ago, or the long term dates+times. And trying to catch 'last seen' and not the other relativetimes. (Thanks Dennis).

NB. curl is a default alias for Invoke-WebRequest, it's not the standard curl program ported to Windows.

TessellatingHeckler

Posted 2015-10-05T17:39:12.293

Reputation: 2 412

2There's also secs ago, 1 min ago (no s), and I think I've seen just now. – Dennis – 2015-10-06T06:00:44.753

@Dennis I think that's fixed now, thanks. (at the cost of falling behind your Bash answer; maybe I should try shamelessly using your pattern matches...). – TessellatingHeckler – 2015-10-06T06:18:54.777

7

Javascript ES6, 234 bytes

document.write(`<script src="//api.stackexchange.com/users/${prompt(a=d=>{n=(x=d.items[0]).display_name,alert((Date.now()/1e3)-x.last_access_date>86400?`Sadly, ${n} is dead.`:n+' is alive!')})}?site=codegolf&callback=a">\x3C/script>`)

Annotated version

// Inserts a script tag to perform a JSONP callback request on the stackexchange API
document.write(`
  <script src="//api.stackexchange.com/users/${
    prompt( // interpolate user input into url
      a = d =>{ // declare a in global scope
        n = (x = d.items[0]).display_name, // alias the user object and name
        alert(
          (Date.now() / 1e3) - x.last_access_date > 86400 
          ? `Sadly, ${n} is dead.` // a day or less since last seen
          : n + ' is alive!' // more than a day since last seen
        )
      }
    )
  }?site=codegolf&callback=a">\x3C/script>` // escaping that prevents early termination of enclosing script tag
)

George Reith

Posted 2015-10-05T17:39:12.293

Reputation: 2 424

Fails to parse apostrophe in my name, yet correctly parses every other character. XD +1 for in-browser testing – Conor O'Brien – 2015-10-06T01:30:17.490

1@CᴏɴᴏʀO'Bʀɪᴇɴ Damn the API entity encodes it. – George Reith – 2015-10-06T01:35:54.123

5

AutoIt, 320 316 308 bytes

#include<String.au3>
#include<Date.au3>
$0=_StringBetween
$1=BinaryToString(InetRead('http://codegolf.xyz/u/'&ClipGet()))
$2=_DateDiff('D',StringReplace($0($1,'Last seen <span title="',' ')[0],'-','/'),@YEAR&'/'&@MON&'/'&@MDAY)
ConsoleWrite(($2?'Sadly, ':'')&$0($1,'r ','- P')[0]&'is '&($2?'dead.':'alive!'))

_DateDiff calcs the difference in days ('D'). It will be 0 if the difference is less than 1 day, so we can use it as a boolean value. The title tag of the "last seen" value contains a (almost) standard timestamp.

mınxomaτ

Posted 2015-10-05T17:39:12.293

Reputation: 7 398

2I haven't seen auto it in over a decade. Lord, take my upvote. – Qix - MONICA WAS MISTREATED – 2015-10-06T04:34:43.850

5

R, 384 350 bytes

This one's for you, Peter!

u=scan();D=as.POSIXlt;J=jsonlite::fromJSON(gsub("/\\*\\*/a|[()]|;$","",httr::content(httr::GET(paste0("http://api.stackexchange.com/2.2/users/",u,"?site=codegolf&callback=a")),,"text")))$items;l=D(J$last_access_date,z<-"UTC","1970-01-01");n=D(Sys.time(),z);U=J$display_name;if(as.Date(n)-as.Date(l)>1)cat("Sadly,",U,"is dead.")else cat(U,"is alive!")

Note that this requires the httr and jsonlite packages to be installed, though they don't have to be imported for this code to work since we're referencing namespaces explicitly.

Ungolfed:

# Read a user ID from STDIN
u <- scan()

# Create a request object using the SE API v2.2
request <- httr::GET(paste0("http://api.stackexchange.com/2.2/users/", u,
                            "?site=codegolf&callback=a"))

# Read the contents of the request into a ill-formed JSON string
body <- httr::content(request, type = "text")

# Parse out a valid string and get the associated fields
J <- jsonlite::fromJSON(gsub("/\\*\\*/a|[()]|;$", "", body))$items

# Get the last accessed date as a POSIX datetime object
l <- as.POSIXlt(J$last_access_date, "UTC", "1970-01-01")

# Get the current date
n <- as.POSIXlt(Sys.time(), "UTC")

# Extract the username
U <- J$display_name

# Determine whether the user has died
if (as.Date(n) - as.Date(l) > 1) {
    cat("Sadly," U, "is dead.")
} else {
    cat(U, "is alive!")
}

Saved 5 bytes on my previous approach and corrected an error in my current approach thanks to minxomat!

Alex A.

Posted 2015-10-05T17:39:12.293

Reputation: 23 761

I dunno about R, but can't u save some bytes by putting the string literal "/users/" into a variable? – mınxomaτ – 2015-10-05T19:40:14.427

@minxomat Sure can, thanks! – Alex A. – 2015-10-05T19:44:08.907

\n and ; take the same amount of bytes. So your code doesn't have to look like that – OganM – 2015-10-05T22:36:52.047

@OganM Actually, in R, scan() will think succeeding lines are the stuff you want to enter from STDIN (who knows why), so the semicolons are required in this case. But in general you're right. – Alex A. – 2015-10-06T00:09:34.553

1

PHP, 187 bytes

Fairly simplistic approach, using the codegolf.xyz domain, only slightly different item here is that I attempt to get both variables at once. Tested on a few users with correct results, please let me know if there are some problem areas though!

<?preg_match('/"User ([^"]+)".+?"([^"]+)" class="r/s',file_get_contents("http://codegolf.xyz/u/$argv[1]"),$m);echo time()-strtotime($m[2])<86400?"$m[1] is alive!":"Sadly, $m[1] is dead.";

Usage:

php 59763.php 30525

Dom Hastings

Posted 2015-10-05T17:39:12.293

Reputation: 16 415

$m[1] is appears two times. Wouldn't it be cheaper to print this first and use the ternary just for the dead/alive part? – mınxomaτ – 2015-10-06T18:33:17.320

@minxomat I did play with that, but the Sadly, ... part got in the way... Can't seem to get my head around it! – Dom Hastings – 2015-10-06T18:44:58.490

Echo the Sadly first, then assign the result from time()-strtotime($m[2]) to a single-char variable. Then echo $m[1] is and lastly the dead/alive. That's how I did it - dunno if this will work in PHP :) – mınxomaτ – 2015-10-06T18:49:06.630

@minxomat Ahhh, so simple, But I couldn't get my head around it! Thanks! I'll update as soon as I can test again! – Dom Hastings – 2015-10-06T18:52:31.127

1

Groovy, 355 bytes

import groovy.json.JsonSlurper;import java.util.zip.GZIPInputStream;def d = new JsonSlurper().parseText(new GZIPInputStream(new URL("http://api.stackexchange.com/2.2/users/${args[0]}?site=codegolf").newInputStream()).getText()).items[0];def n = d.display_name;println d.last_access_date*1000l<new Date().time-8.64E7?"Sadly, ${n} is dead.":"${n} is alive!"

Uncompressed source

import groovy.json.JsonSlurper;
import java.util.zip.GZIPInputStream;

def rawText = new GZIPInputStream(new URL("http://api.stackexchange.com/2.2/users/${args[0]}?site=codegolf").newInputStream()).getText()
def json = new JsonSlurper().parseText(rawText).items[0]
def name = json.display_name
//We have to correct for java date returning in millis
def lastAccess = json.last_access_date * 1000l
def yesterday = new Date().time - 86400000
if (lastAccess < yesterday) {
    println "Sadly, ${name} is dead."
} else {
    println "${name} is alive!"
}

Mike Clark

Posted 2015-10-05T17:39:12.293

Reputation: 171

You can save 42 bytes (brings it down to 313 bytes) if you just do new groovy.json.JsonSlurper and new java.util.zip.GZIPInputStream instead of the imports. Importing only typically pays off if you use a class a lot. – a spaghetto – 2015-10-11T21:07:30.540