├── .gitignore ├── LICENSE ├── README ├── bf3.py ├── defaults.cfg ├── manage.py ├── requirements.txt ├── static ├── favicon.png ├── header.png ├── pattern-box.png ├── pattern-bright.png ├── pattern-tweet.png ├── pattern.png ├── script.js ├── star.png ├── steam-signin.png ├── style.css └── twitter.png └── templates ├── _helpers.html ├── about.html ├── admin.html ├── developers.html ├── favorites.html ├── layout.html ├── listing.html ├── maintenance.html ├── no_javascript.html ├── page_not_found.html ├── show_all.html ├── show_developer.html ├── show_forums.html └── show_twitter.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | local.cfg 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 by Armin Ronacher, see AUTHORS for more details. 2 | 3 | Some rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials provided 15 | with the distribution. 16 | 17 | * The names of the contributors may not be used to endorse or 18 | promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | BF 3 News Aggregator 2 | 3 | Running on bf3.immersedcode.org 4 | 5 | Currently requires Werkzeug==dev 6 | 7 | Things to do: 8 | 9 | 1. create virtualenv 10 | 2. install dependencies with `pip install -r requirements.txt` 11 | 3. create a local.cfg: 12 | 13 | SQLALCHEMY_DATABASE_URI = 'sqlite:////path/to/bf3.db' 14 | FORUM_USERNAME = '...' 15 | FORUM_PASSWORD = '...' 16 | -------------------------------------------------------------------------------- /bf3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | bf3 5 | ~~~ 6 | 7 | Battlefield3 aggregator script thingy. Uses logging from the stdlib and 8 | something has to configure the logger before this can safely be used. 9 | 10 | :copyright: (c) Copyright 2011 by Armin Ronacher. 11 | :license: BSD, see LICENSE for more details. 12 | """ 13 | import re 14 | import urllib2 15 | import cookielib 16 | import html5lib 17 | import logging 18 | from twitter_text import TwitterText 19 | from datetime import datetime, timedelta 20 | from urlparse import urljoin 21 | from functools import update_wrapper 22 | from flask import Flask, Markup, render_template, json, request, url_for, \ 23 | redirect, jsonify, g, session, flash, abort 24 | from flaskext.sqlalchemy import SQLAlchemy 25 | from flaskext.openid import OpenID 26 | from werkzeug.urls import url_decode, url_encode, url_quote 27 | from werkzeug.http import parse_date, http_date 28 | from werkzeug.utils import cached_property 29 | 30 | from werkzeug.contrib.atom import AtomFeed 31 | 32 | 33 | app = Flask(__name__) 34 | app.config.from_pyfile('defaults.cfg') 35 | app.config.from_pyfile('local.cfg') 36 | db = SQLAlchemy(app) 37 | oid = OpenID(app) 38 | 39 | 40 | # set up the logging system based on debug settings 41 | if app.debug: 42 | logging.basicConfig(level=logging.DEBUG) 43 | else: 44 | from logging.handlers import SMTPHandler 45 | mail_handler = SMTPHandler(app.config['MAIL_SERVER'], 46 | app.config['ERROR_MAIL_SENDER'], 47 | app.config['ADMINS'], 48 | app.config['ERROR_MAIL_SUBJECT']) 49 | mail_handler.setFormatter(logging.Formatter('''\ 50 | Message type: %(levelname)s 51 | Location: %(pathname)s:%(lineno)d 52 | Module: %(module)s 53 | Function: %(funcName)s 54 | Time: %(asctime)s 55 | 56 | Message: 57 | 58 | %(message)s 59 | ''')) 60 | root_logger = logging.getLogger() 61 | root_logger.setLevel(logging.ERROR) 62 | root_logger.addHandler(mail_handler) 63 | 64 | 65 | _security_token_re = re.compile(r'var SECURITYTOKEN\s+=\s+"([^"]+)"') 66 | _post_reference_re = re.compile( 67 | r'' 68 | r'\s+ 18 | {% endmacro %} 19 | -------------------------------------------------------------------------------- /templates/about.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html' %} 2 | {% block title %}About this Website{% endblock %} 3 | {% block body %} 4 |

About this Website

5 |
6 |

What's this?

7 |

8 | This website aggregates the official battlefield 3 forums and the twitter 9 | accounts of developers for news from the sources. It updates in 15 10 | minute intervals and aggregates most developer accounts. In case you think 11 | I am missing one, shoot me a line 12 | and I will add him or her. 13 |

Who is behind this?

14 |

15 | Armin Ronacher. You can contact me 16 | via mail or 17 | Twitter. 18 |

Why does this website exist?

19 |

20 | Following the twitter accounts of all developers is not the problem, but 21 | also finding what they write into the forums is too much work. However every 22 | once in a while there is some interesting information in the forums which 23 | is why I decided to automatically collect that information. 24 |

There is too much information

25 |

26 | Indeed. If you have some ideas how I could filter out useless tweets and 27 | stuff, let me know. I am already filtering out replies to other people 28 | from the tweets which reduced the amount of information here a lot, but it's 29 | still too much. I personally mostly use it to track the forum posts which 30 | are otherwise quite hard to find. 31 |

I am missing on this list / X is missing on this list

32 |

33 | This website aggregates developers working on BF3 only. If you are 34 | missing or someone you know is missing, please let me know and provide 35 | this information: real name, twitter account, forums account, optionally 36 | a short description. If that developer does not have a twitter or forum 37 | account, one is enough obviously. 38 |

Where is the Code?

39 |

40 | Good that you're asking. If you want to have a look at it, you can find the 41 | code on github: mitsuhiko/bf3-aggregator. 42 |

Can I get that data?

43 |

44 | Send an HTTP request with Accept: application/json to any of the 45 | URLs returning tweets or forum posts and you get JSON back. That you can parse 46 | and use for your own purposes. This website updates every ~30 minutes, so don't 47 | bother requesting more. I don't have unlimited traffic on this server so please 48 | be kind and cache on your side. 49 |

Why sign in with Steam?

50 |

51 | If you are logged in you can favorite items. This has the advantage that if 52 | enough users do that I can filter out more interesting items. You will also be 53 | able to find again items you found interesting in the past. 54 |

55 | I would have linked to use the EA account system for that, but unfortunately 56 | they don't provide a service like steam which allows me to securely use the 57 | authentication system here. If you sign in with steam on this website no 58 | account information is transmitted to this website besides a unique number 59 | that keeps you apart from others and your current username (as well as real 60 | name if you have specified it). 61 |

62 | If you don't have a steam account yet, you can create one for free 63 | on the steam website and 64 | use that to log into this website. 65 |

66 | {% endblock %} 67 | -------------------------------------------------------------------------------- /templates/admin.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html' %} 2 | {% block title %}Admin{% endblock %} 3 | {% macro render_dev_box(dev) %} 4 | {% endmacro %} 5 | {% block body %} 6 |

Admin

7 |
8 |
9 |

Developers

10 | {%- for dev in developers %} 11 |
12 |
Name: 13 |
14 |
Twitter: 15 |
16 |
Forums: 17 |
18 |
Description: 19 |
20 |
21 | {%- endfor %} 22 |

Add Developer

23 |
24 |
Name: 25 |
26 |
27 |

28 |

29 |
30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /templates/developers.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html' %} 2 | {% block title %}Developers{% endblock %} 3 | {% block body %} 4 |

Developers

5 |
6 |

7 | The following developers are currently tracker by this website: 8 |

15 |

16 | Someone is missing? let me know. 17 |

18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /templates/favorites.html: -------------------------------------------------------------------------------- 1 | {% extends 'listing.html' %} 2 | {% set feed_url = url_for('feed', source='fav', slug=user.slug) %} 3 | {% block title %}{{ user.nickname }}'s Favorited Battlefield 3 News{% endblock %} 4 | {% block listing_body %} 5 | {% if not pagination.items %} 6 |

7 | No favorited items so far. 8 | {% elif g.user == user %} 9 |

10 | Link to this page to share your favorited items with others. 11 | {% endif %} 12 | {{ super() }} 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | {% block title %}{% endblock %} | Battlefield3 Development News Aggregator 3 | 4 | 5 | 6 | 9 | 10 | {%- if feed_url is defined %} 11 | 12 | {%- endif %} 13 | {%- for message in get_flashed_messages() %} 14 |

{{ message }}
15 | {%- endfor %} 16 |
17 |
18 |

Battlefield 3 Development News Aggregator

19 |
20 |

21 | {%- if not g.user %} 22 | 24 | {%- else %} 25 | Currently logged in as {{ g.user.nickname }} | 26 | favorited news | 27 | logout 28 | {%- endif %} 29 |

41 |
42 |
43 | {% block body %}{% endblock %} 44 |
45 | 56 |
57 |
58 | 59 | 70 | -------------------------------------------------------------------------------- /templates/listing.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html' %} 2 | {% from '_helpers.html' import render_pagination %} 3 | {% block body %} 4 |

{{ self.title() }}

5 | {% block listing_body %} 6 | 33 | 34 | {% if feed_url is defined %} 35 |
36 | Feed 37 |
38 | {% endif %} 39 | {{ render_pagination(pagination) }} 40 | {% endblock %} 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /templates/maintenance.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html' %} 2 | {% block title %}Site Maintenance{% endblock %} 3 | {% block body %} 4 |

Site Maintenance

5 |

6 | I'm rolling out an update right now. Check back in a couple 7 | of minutes. 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /templates/no_javascript.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html' %} 2 | {% block title %}Error: No Javascript Enabled{% endblock %} 3 | {% block body %} 4 |

{{ self.title() }}

5 |
6 |

Awwww

7 |

8 | You have disabled JavaScript in your browser. The feature you 9 | tried to use however requires that JavaScript is available. Please 10 | make sure to enable it for this website and hit the back button. 11 |

12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /templates/page_not_found.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html' %} 2 | {% block title %}Page Not Found{% endblock %} 3 | {% block body %} 4 |

Page Not Found

5 |
6 |

7 | The page you are looking for does not exist. You might 8 | want to consider returning to 9 | the newsfeed. 10 |

11 | If you believe this is a mistake 12 | contact the admin. 13 |

14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /templates/show_all.html: -------------------------------------------------------------------------------- 1 | {% extends 'listing.html' %} 2 | {% set feed_url = url_for('feed', source='all') %} 3 | {% block title %}All Items{% endblock %} 4 | -------------------------------------------------------------------------------- /templates/show_developer.html: -------------------------------------------------------------------------------- 1 | {% extends 'listing.html' %} 2 | {% set feed_url = url_for('feed', source='developer', slug=developer.slug) %} 3 | {% block title %}News by “{{ developer.name }}”{% endblock %} 4 | {% block listing_body %} 5 |
6 | {%- if developer.twitter_name %} 7 |
Twitter name 8 |
@{{ developer.twitter_name }} 10 | {%- endif %} 11 | {%- if developer.forum_name %} 12 |
Forum name 13 |
{{ developer.forum_name }} 14 | {%- endif %} 15 |
Description 16 |
{{ developer.description_html }} 17 |
18 | {{ super() }} 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /templates/show_forums.html: -------------------------------------------------------------------------------- 1 | {% extends 'listing.html' %} 2 | {% set feed_url = url_for('feed', source='forums') %} 3 | {% block title %}Developer Forum Posts{% endblock %} 4 | -------------------------------------------------------------------------------- /templates/show_twitter.html: -------------------------------------------------------------------------------- 1 | {% extends 'listing.html' %} 2 | {% set feed_url = url_for('feed', source='twitter') %} 3 | {% block title %}Latest Tweets{% endblock %} 4 | {% block listing_body %} 5 |

Show replies: 6 | {% for with_replies, caption in [(True, 'Yes'), (False, 'No')] %} 7 | {% if not loop.first %}|{% endif %} 8 | {{ caption }} 11 | {% endfor %} 12 | 13 | {{ super() }} 14 | {% endblock %} 15 | --------------------------------------------------------------------------------