As with many problems with Drupal integration civcrm_entity is the answer.
Install the latest civicrm_entity module for Drupal in the usual way (Recommend using Drush if you can).You'll also need to enable the civicrm_entity_views_extras sub module.
We'll be using drupal views to display the results so if you have your Drupal and Civi in different databases then make sure you have civicrm cms integration configured. Go to Civicrm home and choose
Administer->System Settings->CMS database integration. This will bring up a page of PHP code to paste into your current drupal settings.php to allow drupal views to fetch civicrm data. You might need to change permissions on settings.php and it's enclosing folder to edit the file, remember to change them back.
Now, create a new view to show "civicrm events", click "Create a page" and choose "unformatted list of fields" as Display format.
This will show all events in the system. For our purpose we only wanted to show events on a certain afternoon, so we added a "filter criteria" to only show events starting in the correct timeframe. Worked for us but you may need to find other filtering techniques for other purposes.
Add fields for all the information you want to display. Title, description etc.
The field that does the magic is called "Civicrm Events:event is full" this will return a 0 if there are spots left or 1 if the event is full.
So now your view knows when an event is full, to change what is displayed to the user you'll need another module—views conditional—install that and reload the view editor.
Add a views conditional field below all other fields and set it up so it checks if the event is full field and returns either the "registration link" or a custom message.
This should now be working, but you're still seeing the "Registration link text field" and "event is full" field above your conditional output. Click to change the settings of each one to "exclude from display" and check that "registration link text" is linking to where you want it. The setting sometimes seems to reset when I save the view.